Merge branch 'capitalizing_settings' into direct_boot_aware

# Conflicts:
#	app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/ShiftState.kt
#	app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt
This commit is contained in:
merkost 2023-06-09 10:00:24 +10:00
commit 21a22092fb
4 changed files with 26 additions and 158 deletions

View File

@ -1,5 +1,12 @@
package com.simplemobiletools.keyboard.helpers package com.simplemobiletools.keyboard.helpers
enum class ShiftState {
OFF,
ON_ONE_CHAR,
ON_PERMANENT;
}
// 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
const val MAX_KEYS_PER_MINI_ROW = 9 const val MAX_KEYS_PER_MINI_ROW = 9

View File

@ -242,12 +242,7 @@ class MyKeyboard {
fun isInside(x: Int, y: Int): Boolean { fun isInside(x: Int, y: Int): Boolean {
val leftEdge = edgeFlags and EDGE_LEFT > 0 val leftEdge = edgeFlags and EDGE_LEFT > 0
val rightEdge = edgeFlags and EDGE_RIGHT > 0 val rightEdge = edgeFlags and EDGE_RIGHT > 0
return ( return ((x >= this.x || leftEdge && x <= this.x + width) && (x < this.x + width || rightEdge && x >= this.x) && (y >= this.y && y <= this.y + height) && (y < this.y + height && y >= this.y))
(x >= this.x || leftEdge && x <= this.x + width) &&
(x < this.x + width || rightEdge && x >= this.x) &&
(y >= this.y && y <= this.y + height) &&
(y < this.y + height && y >= this.y)
)
} }
} }
@ -258,14 +253,13 @@ 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, shiftState: ShiftState = ShiftState.OFF) { constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int) {
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))
} }
@ -278,8 +272,7 @@ class MyKeyboard {
* @param characters the list of characters to display on the keyboard. One key will be created for each character. * @param characters the list of characters to display on the keyboard. One key will be created for each character.
* @param keyWidth the width of the popup key, make sure it is the same as the key itself * @param keyWidth the width of the popup key, make sure it is the same as the key itself
*/ */
constructor(context: Context, layoutTemplateResId: Int, characters: CharSequence, keyWidth: Int) : constructor(context: Context, layoutTemplateResId: Int, characters: CharSequence, keyWidth: Int) : this(context, layoutTemplateResId, 0) {
this(context, layoutTemplateResId, 0) {
var x = 0 var x = 0
var y = 0 var y = 0
var column = 0 var column = 0

View File

@ -1,81 +0,0 @@
package com.simplemobiletools.keyboard.helpers
import android.content.Context
import android.text.InputType
import com.simplemobiletools.keyboard.extensions.config
enum class ShiftState {
OFF,
ON_ONE_CHAR,
ON_PERMANENT;
companion object {
private val endOfSentenceChars: List<Char> = listOf('.', '?', '!')
private const val MIN_TEXT_LENGTH = 2
/**
* Input Type exceptions for capitalizing.
*/
private val inputTypeExceptions = listOf(
InputType.TYPE_TEXT_VARIATION_PASSWORD,
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,
InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD,
InputType.TYPE_NUMBER_VARIATION_PASSWORD,
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS,
InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS,
InputType.TYPE_TEXT_VARIATION_URI
)
fun getDefaultShiftState(context: Context, inputTypeClassVariation: Int): ShiftState {
if (isInputTypeAllowedCapitalizing(inputTypeClassVariation)) {
return OFF
}
return if (context.config.enableSentencesCapitalization) {
ON_ONE_CHAR
} else {
OFF
}
}
fun getShiftStateForText(context: Context, inputTypeClassVariation: Int, text: String?): ShiftState {
if (isInputTypeAllowedCapitalizing(inputTypeClassVariation)) {
return OFF
}
return if (shouldCapitalize(context, text)) {
ON_ONE_CHAR
} else {
OFF
}
}
/**
* The function is checking whether there is a need in capitalizing based on the given text
* @param context Used for checking current sentences capitalization setting
* @param text Last text from the input
*/
fun shouldCapitalize(context: Context, text: String?): Boolean {
// check whether it is the first letter in textField
if (text.isNullOrEmpty()) {
return true
}
if (!context.config.enableSentencesCapitalization) {
return false
}
val twoLastSymbols = text.takeLast(2)
if (twoLastSymbols.length < MIN_TEXT_LENGTH) {
return false
}
return endOfSentenceChars.contains(twoLastSymbols.first()) && twoLastSymbols.last().code == MyKeyboard.KEYCODE_SPACE
}
fun isInputTypeAllowedCapitalizing(inputTypeVariation: Int): Boolean {
return inputTypeVariation in inputTypeExceptions
}
}
}

View File

@ -2,7 +2,6 @@ package com.simplemobiletools.keyboard.services
import android.content.SharedPreferences import android.content.SharedPreferences
import android.inputmethodservice.InputMethodService import android.inputmethodservice.InputMethodService
import android.text.InputType
import android.text.InputType.* import android.text.InputType.*
import android.text.TextUtils import android.text.TextUtils
import android.view.KeyEvent import android.view.KeyEvent
@ -18,8 +17,7 @@ import com.simplemobiletools.keyboard.extensions.config
import com.simplemobiletools.keyboard.extensions.safeStorageContext import com.simplemobiletools.keyboard.extensions.safeStorageContext
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.keyboard_holder import kotlinx.android.synthetic.main.keyboard_view_keyboard.view.*
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 {
@ -34,8 +32,8 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
private var keyboardView: MyKeyboardView? = null private var keyboardView: MyKeyboardView? = null
private var lastShiftPressTS = 0L private var lastShiftPressTS = 0L
private var keyboardMode = KEYBOARD_LETTERS private var keyboardMode = KEYBOARD_LETTERS
private var inputTypeClass = InputType.TYPE_CLASS_TEXT private var inputTypeClass = TYPE_CLASS_TEXT
private var inputTypeClassVariation = InputType.TYPE_CLASS_TEXT private var inputTypeClassVariation = TYPE_CLASS_TEXT
private var enterKeyType = IME_ACTION_NONE private var enterKeyType = IME_ACTION_NONE
private var switchToLetters = false private var switchToLetters = false
@ -63,7 +61,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
override fun onStartInput(attribute: EditorInfo?, restarting: Boolean) { override fun onStartInput(attribute: EditorInfo?, restarting: Boolean) {
super.onStartInput(attribute, restarting) super.onStartInput(attribute, restarting)
inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS
inputTypeClassVariation = attribute!!.inputType and TYPE_MASK_VARIATION inputTypeClassVariation = attribute.inputType and TYPE_MASK_VARIATION
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 = createNewKeyboard()
keyboardView?.setKeyboard(keyboard!!) keyboardView?.setKeyboard(keyboard!!)
@ -72,47 +70,17 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
} }
private fun updateShiftKeyState(code: Int?) { private fun updateShiftKeyState(code: Int?) {
if (keyboardMode != KEYBOARD_LETTERS || ShiftState.isInputTypeAllowedCapitalizing(inputTypeClassVariation)) { if (code == MyKeyboard.KEYCODE_SHIFT) {
return return
} }
if (code == MyKeyboard.KEYCODE_SHIFT || code == MyKeyboard.KEYCODE_DELETE) { val editorInfo = currentInputEditorInfo
return if (config.enableSentencesCapitalization && editorInfo != null && editorInfo.inputType != TYPE_NULL) {
} if (currentInputConnection.getCursorCapsMode(editorInfo.inputType) != 0) {
keyboard?.setShifted(ShiftState.ON_ONE_CHAR)
val text = currentInputConnection.getTextBeforeCursor(2, 0) ?: return
// capitalize first letter on startup or if text is empty
if (code == null || text.isEmpty()) {
keyboard!!.setShifted(ShiftState.ON_ONE_CHAR)
keyboardView?.invalidateAllKeys()
return
}
// capitalize sentences if needed
if (config.enableSentencesCapitalization) {
// capitalize on Enter click
if (code == MyKeyboard.KEYCODE_ENTER) {
keyboard!!.setShifted(ShiftState.ON_ONE_CHAR)
keyboardView?.invalidateAllKeys() keyboardView?.invalidateAllKeys()
return return
} }
if (ShiftState.shouldCapitalize(this, text.toString())) {
keyboard!!.setShifted(ShiftState.ON_ONE_CHAR)
keyboardView?.invalidateAllKeys()
return
} else {
// try capitalizing based on the editor info like google keep or google messenger apps
if (currentInputEditorInfo != null && currentInputEditorInfo.inputType != InputType.TYPE_NULL) {
if (currentInputConnection.getCursorCapsMode(currentInputEditorInfo.inputType) != 0) {
keyboard?.setShifted(ShiftState.ON_ONE_CHAR)
keyboardView?.invalidateAllKeys()
return
}
}
}
} }
keyboard?.setShifted(ShiftState.OFF) keyboard?.setShifted(ShiftState.OFF)
@ -131,25 +99,14 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
when (code) { when (code) {
MyKeyboard.KEYCODE_DELETE -> { MyKeyboard.KEYCODE_DELETE -> {
val selectedText = inputConnection.getSelectedText(0) val selectedText = inputConnection.getSelectedText(0)
if (TextUtils.isEmpty(selectedText)) { if (TextUtils.isEmpty(selectedText)) {
val text = inputConnection.getTextBeforeCursor(3, 0)?.dropLast(1)
if (keyboard?.mShiftState != ShiftState.ON_PERMANENT) {
keyboard?.setShifted(ShiftState.getShiftStateForText(this, inputTypeClassVariation, text?.toString()))
keyboardView?.invalidateAllKeys()
}
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL))
} else { } else {
inputConnection.commitText("", 1) inputConnection.commitText("", 1)
} }
} }
MyKeyboard.KEYCODE_SHIFT -> { MyKeyboard.KEYCODE_SHIFT -> {
if (keyboardMode == KEYBOARD_LETTERS) { if (keyboardMode == KEYBOARD_LETTERS) {
when { when {
@ -173,7 +130,6 @@ 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) {
@ -183,7 +139,6 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)) inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
} }
} }
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
@ -195,11 +150,9 @@ 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 val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return
@ -211,7 +164,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
// If the keyboard is set to symbols and the user presses space, we usually should switch back to the letters keyboard. // If the keyboard is set to symbols and the user presses space, we usually should switch back to the letters keyboard.
// 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 && inputTypeClass == TYPE_CLASS_TEXT && code == MyKeyboard.KEYCODE_SPACE) {
inputConnection.commitText(codeChar.toString(), 1) inputConnection.commitText(codeChar.toString(), 1)
val newText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text val newText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text
if (originalText != newText) { if (originalText != newText) {
@ -222,6 +175,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
} }
} }
} }
if (keyboard!!.mShiftState != ShiftState.ON_PERMANENT) { if (keyboard!!.mShiftState != ShiftState.ON_PERMANENT) {
updateShiftKeyState(code) updateShiftKeyState(code)
} }
@ -232,14 +186,10 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
// TODO: Change keyboardMode to enum class // TODO: Change keyboardMode to enum class
keyboardMode = KEYBOARD_LETTERS keyboardMode = KEYBOARD_LETTERS
//Check if capitalization is needed after switching to letters layout keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType)
val text = currentInputConnection?.getTextBeforeCursor(2, 0)
val newShiftState = ShiftState.getShiftStateForText(this, inputTypeClassVariation, text?.toString())
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 != TYPE_NULL && keyboard?.mShiftState != ShiftState.ON_PERMANENT) {
if (currentInputConnection.getCursorCapsMode(editorInfo.inputType) != 0) { if (currentInputConnection.getCursorCapsMode(editorInfo.inputType) != 0) {
keyboard?.setShifted(ShiftState.ON_ONE_CHAR) keyboard?.setShifted(ShiftState.ON_ONE_CHAR)
} }
@ -274,24 +224,23 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
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( return MyKeyboard(
context = this, xmlLayoutResId = keyboardXml, enterKeyType = enterKeyType, shiftState = ShiftState.getDefaultShiftState(this, inputTypeClassVariation) context = this,
xmlLayoutResId = keyboardXml,
enterKeyType = enterKeyType,
) )
} }