Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/src/main/java/be/scri/helpers/KeyHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import be.scri.services.GeneralKeyboardIME.ScribeState
class KeyHandler(
private val ime: GeneralKeyboardIME,
) {
private val suggestionHandler = SuggestionHandler(ime)
private val suggestionHandler = ime.suggestionHandler
private val spaceKeyProcessor = SpaceKeyProcessor(ime, suggestionHandler)
private val autocompletionHandler = AutocompletionHandler(ime)
private val autocompletionHandler = ime.autocompletionHandler

/** Tracks if the last key pressed was a space, used for "period on double space" logic. */
private var wasLastKeySpace: Boolean = false
Expand Down
40 changes: 33 additions & 7 deletions app/src/main/java/be/scri/helpers/ui/KeyboardUIManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,17 @@ class KeyboardUIManager(

fun getKeyboardLayoutXML(): Int

fun getCurrentKeyboardLayoutXML(): Int

fun getCurrentEnterKeyType(): Int

fun commitText(text: String)

fun onKeyboardActionListener(): KeyboardView.OnKeyboardActionListener

fun processLinguisticSuggestions(word: String)

fun isNumericKeyboardActive(): Boolean
}

var keyboardView: KeyboardView = binding.keyboardView
Expand Down Expand Up @@ -159,7 +163,12 @@ class KeyboardUIManager(
val isUserDarkMode = getIsDarkModeOrNot(context)

when (currentState) {
ScribeState.IDLE -> setupIdleView(language, emojiAutoSuggestionEnabled, autoSuggestEmojis)
ScribeState.IDLE ->
setupIdleView(
language,
emojiAutoSuggestionEnabled,
autoSuggestEmojis,
)
ScribeState.SELECT_COMMAND -> setupSelectCommandView(language)
ScribeState.INVALID -> setupInvalidView(language, invalidCommandSource)
ScribeState.TRANSLATE -> {
Expand All @@ -184,7 +193,7 @@ class KeyboardUIManager(
emojiAutoSuggestionEnabled: Boolean,
autoSuggestEmojis: MutableList<String>?,
) {
binding.commandOptionsBar.visibility = View.VISIBLE
binding.commandOptionsBar.visibility = if (listener.isNumericKeyboardActive()) View.GONE else View.VISIBLE
binding.toolbarBar.visibility = View.GONE

val isUserDarkMode = getIsDarkModeOrNot(context)
Expand Down Expand Up @@ -226,7 +235,11 @@ class KeyboardUIManager(

binding.scribeKeyOptions.foreground = AppCompatResources.getDrawable(context, R.drawable.ic_scribe_icon_vector)

initializeKeyboard(listener.getKeyboardLayoutXML())
val keyboardXml = listener.getCurrentKeyboardLayoutXML()
initializeKeyboard(keyboardXml)
if (keyboardXml == R.xml.keys_symbols) {
setupCurrencySymbol(language)
}

updateButtonVisibility(ScribeState.IDLE, emojiAutoSuggestionEnabled, autoSuggestEmojis)
updateEmojiSuggestion(ScribeState.IDLE, emojiAutoSuggestionEnabled, autoSuggestEmojis)
Expand All @@ -239,7 +252,7 @@ class KeyboardUIManager(
* (Translate, Conjugate, Plural).
*/
private fun setupSelectCommandView(language: String) {
binding.commandOptionsBar.visibility = View.VISIBLE
binding.commandOptionsBar.visibility = if (listener.isNumericKeyboardActive()) View.GONE else View.VISIBLE
binding.toolbarBar.visibility = View.GONE

val isUserDarkMode = getIsDarkModeOrNot(context)
Expand Down Expand Up @@ -786,6 +799,8 @@ class KeyboardUIManager(
* Disables all auto-suggestions and resets the suggestion buttons to their default, inactive state.
*/
fun disableAutoSuggest(language: String) {
val isNumericKeyboard = listener.getCurrentKeyboardLayoutXML() == R.xml.keys_numeric

binding.translateBtnRight.visibility = View.INVISIBLE
binding.translateBtnLeft.visibility = View.INVISIBLE
binding.translateBtn.visibility = View.VISIBLE
Expand All @@ -801,9 +816,20 @@ class KeyboardUIManager(
binding.translateBtn.background = null
binding.translateBtn.setOnClickListener(createSuggestionClickListener(suggestion1))

val suggestion2 = suggestions.getOrNull(1) ?: ""
binding.conjugateBtn.text = suggestion2
binding.conjugateBtn.setOnClickListener(createSuggestionClickListener(suggestion2))
if (isNumericKeyboard) {
binding.conjugateBtn.text = ""
binding.conjugateBtn.setOnClickListener(null)
binding.conjugateBtn.visibility = View.GONE
binding.separator2.visibility = View.GONE
binding.separator3.visibility = View.GONE
} else {
val suggestion2 = suggestions.getOrNull(1) ?: ""
binding.conjugateBtn.visibility = View.VISIBLE
binding.conjugateBtn.text = suggestion2
binding.conjugateBtn.setOnClickListener(createSuggestionClickListener(suggestion2))
binding.separator2.visibility = View.VISIBLE
binding.separator3.visibility = View.VISIBLE
}

val suggestion3 = suggestions.getOrNull(2) ?: ""
binding.pluralBtn.text = suggestion3
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/be/scri/services/EnglishKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class EnglishKeyboardIME : GeneralKeyboardIME("English") {
override var inputTypeClass: Int = InputType.TYPE_CLASS_TEXT
override var enterKeyType: Int = IME_ACTION_NONE
override var switchToLetters: Boolean = false
override var hasTextBeforeCursor: Boolean = false

private val keyHandler by lazy { KeyHandler(this) }

Expand Down
60 changes: 42 additions & 18 deletions app/src/main/java/be/scri/services/GeneralKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ abstract class GeneralKeyboardIME(
private val shiftPermToggleSpeed: Int = DEFAULT_SHIFT_PERM_TOGGLE_SPEED

private lateinit var dbManagers: DatabaseManagers
private lateinit var suggestionHandler: SuggestionHandler
private lateinit var autocompletionHandler: AutocompletionHandler
internal lateinit var suggestionHandler: SuggestionHandler
internal lateinit var autocompletionHandler: AutocompletionHandler
private lateinit var autocompletionManager: AutocompletionDataManager
private var dataContract: DataContract? = null

Expand All @@ -144,6 +144,7 @@ abstract class GeneralKeyboardIME(
var wordSuggestions: List<String>? = null
var checkIfPluralWord: Boolean = false
private var currentEnterKeyType: Int? = null
private var isNumericKeyboardActive: Boolean = false

internal var currentState: ScribeState = ScribeState.IDLE
internal var invalidCommandSource: ScribeState = ScribeState.IDLE
Expand Down Expand Up @@ -176,6 +177,22 @@ abstract class GeneralKeyboardIME(
internal const val MAX_TEXT_LENGTH = 1000
const val COMMIT_TEXT_CURSOR_POSITION = 1
internal const val CUSTOM_CURSOR = "│" // special tall cursor character

internal fun shouldUseNumericKeyboard(inputType: Int): Boolean =
when (inputType and TYPE_MASK_CLASS) {
TYPE_CLASS_NUMBER, TYPE_CLASS_DATETIME, TYPE_CLASS_PHONE -> true
else -> false
}

internal fun getKeyboardLayoutXMLForInputType(
inputType: Int,
letterKeyboardLayoutXML: Int,
): Int =
if (shouldUseNumericKeyboard(inputType)) {
R.xml.keys_numeric
} else {
letterKeyboardLayoutXML
}
}

enum class ScribeState { IDLE, SELECT_COMMAND, TRANSLATE, CONJUGATE, PLURAL, SELECT_VERB_CONJUNCTION, INVALID, ALREADY_PLURAL }
Expand Down Expand Up @@ -286,25 +303,16 @@ abstract class GeneralKeyboardIME(
// This setter triggers the logic in the property override if not shadowed.
hasTextBeforeCursor = currentInputConnection?.getTextBeforeCursor(1, 0)?.isNotEmpty() == true

val keyboardXml =
when (inputTypeClass) {
TYPE_CLASS_NUMBER, TYPE_CLASS_DATETIME, TYPE_CLASS_PHONE -> {
keyboardMode = keyboardSymbols
R.xml.keys_symbols
}

else -> {
keyboardMode = keyboardLetters
getKeyboardLayoutXML()
}
}
isNumericKeyboardActive = shouldUseNumericKeyboard(attribute.inputType)
keyboardMode = if (isNumericKeyboardActive) keyboardSymbols else keyboardLetters
val keyboardXml = getKeyboardLayoutXMLForInputType(attribute.inputType, getKeyboardLayoutXML())

loadLanguageData()

keyboard = KeyboardBase(this, keyboardXml, enterKeyType)
keyboardView?.setKeyboard(keyboard!!)

if (keyboardXml == R.xml.keys_symbols) {
if (this::uiManager.isInitialized && keyboardXml == R.xml.keys_symbols) {
uiManager.setupCurrencySymbol(language)
}
}
Expand Down Expand Up @@ -334,7 +342,7 @@ abstract class GeneralKeyboardIME(
banner.visibility =
if (hasData) View.GONE else View.VISIBLE
binding.commandOptionsBar.visibility =
if (hasData) View.VISIBLE else View.GONE
if (hasData && !isNumericKeyboardActive) View.VISIBLE else View.GONE
val isDarkMode = getIsDarkModeOrNot(applicationContext)
val bannerColor = if (isDarkMode) R.color.dark_tutorial_button_color else R.color.light_tutorial_button_color
val bannerTextColor = if (isDarkMode) R.color.dark_button_outline_color else R.color.light_text_color
Expand Down Expand Up @@ -727,6 +735,22 @@ abstract class GeneralKeyboardIME(

override fun getCurrentEnterKeyType(): Int = enterKeyType

override fun isNumericKeyboardActive(): Boolean = isNumericKeyboardActive

override fun getCurrentKeyboardLayoutXML(): Int =
when (keyboardMode) {
keyboardSymbols -> getPrimarySymbolKeyboardLayoutXML()
keyboardSymbolShift -> R.xml.keys_symbols_shift
else -> getKeyboardLayoutXML()
}

private fun getPrimarySymbolKeyboardLayoutXML(): Int =
if (isNumericKeyboardActive) {
R.xml.keys_numeric
} else {
R.xml.keys_symbols
}

override fun onKeyboardActionListener(): KeyboardView.OnKeyboardActionListener = this

override fun processLinguisticSuggestions(word: String) {
Expand Down Expand Up @@ -1171,7 +1195,7 @@ abstract class GeneralKeyboardIME(
R.xml.keys_symbols_shift
} else {
this.keyboardMode = keyboardSymbols
R.xml.keys_symbols
getPrimarySymbolKeyboardLayoutXML()
}
keyboard = KeyboardBase(this, keyboardXml, enterKeyType)
keyboardView!!.setKeyboard(keyboard!!)
Expand All @@ -1196,7 +1220,7 @@ abstract class GeneralKeyboardIME(
val keyboardXml =
if (keyboardMode == keyboardLetters) {
this.keyboardMode = keyboardSymbols
R.xml.keys_symbols
getPrimarySymbolKeyboardLayoutXML()
} else {
this.keyboardMode = keyboardLetters
getKeyboardLayoutXML()
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/be/scri/services/ItalianKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class ItalianKeyboardIME : GeneralKeyboardIME("Italian") {
override var inputTypeClass: Int = InputType.TYPE_CLASS_TEXT
override var enterKeyType: Int = IME_ACTION_NONE
override var switchToLetters: Boolean = false
override var hasTextBeforeCursor: Boolean = false

private val keyHandler by lazy { KeyHandler(this) }

Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/be/scri/services/RussianKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class RussianKeyboardIME : GeneralKeyboardIME("Russian") {
override var inputTypeClass: Int = InputType.TYPE_CLASS_TEXT
override var enterKeyType: Int = IME_ACTION_NONE
override var switchToLetters: Boolean = false
override var hasTextBeforeCursor: Boolean = false

private val keyHandler by lazy { KeyHandler(this) }

Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/be/scri/services/SwedishKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ class SwedishKeyboardIME : GeneralKeyboardIME("Swedish") {
override var inputTypeClass: Int = InputType.TYPE_CLASS_TEXT
override var enterKeyType: Int = IME_ACTION_NONE
override var switchToLetters: Boolean = false
override var hasTextBeforeCursor: Boolean = false

private val keyHandler by lazy { KeyHandler(this) }

Expand Down
46 changes: 46 additions & 0 deletions app/src/main/res/xml/keys_numeric.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:app="http://schemas.android.com/apk/res-auto">
<Row app:keyWidth="25%p">
<Key
app:keyEdgeFlags="left"
app:keyLabel="1" />
<Key app:keyLabel="2" />
<Key app:keyLabel="3" />
<Key
app:code="-5"
app:isRepeatable="true"
app:keyEdgeFlags="right"
app:keyIcon="@drawable/ic_clear_outline_vector" />
</Row>
<Row app:keyWidth="25%p">
<Key
app:keyEdgeFlags="left"
app:keyLabel="4" />
<Key app:keyLabel="5" />
<Key app:keyLabel="6" />
<Key app:keyLabel="-" />
</Row>
<Row app:keyWidth="25%p">
<Key
app:keyEdgeFlags="left"
app:keyLabel="7" />
<Key app:keyLabel="8" />
<Key app:keyLabel="9" />
<Key app:keyLabel="+" />
</Row>
<Row app:keyWidth="25%p">
<Key
app:code="-2"
app:keyEdgeFlags="left"
app:keyLabel="ABC" />
<Key app:keyLabel="0" />
<Key
app:keyLabel="."
app:popupCharacters=",/:"
app:popupKeyboard="@xml/keyboard_popup_template" />
<Key
app:code="-4"
app:keyEdgeFlags="right"
app:keyIcon="@drawable/ic_enter_vector" />
</Row>
</Keyboard>
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class KeyboardUIManagerTest {
// Mock Listener
listener = mockk(relaxed = true)
every { listener.getKeyboardLayoutXML() } returns be.scri.R.xml.keys_letters_english
every { listener.getCurrentKeyboardLayoutXML() } returns be.scri.R.xml.keys_letters_english
every { listener.onKeyboardActionListener() } returns mockk()

// Init Manager
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package be.scri.services

import android.text.InputType
import be.scri.R
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class GeneralKeyboardIMEInputTypeTest {
@Test
fun shouldUseNumericKeyboard_returnsTrueForNumberInputs() {
val inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL

assertTrue(GeneralKeyboardIME.shouldUseNumericKeyboard(inputType))
}

@Test
fun shouldUseNumericKeyboard_returnsTrueForDateTimeInputs() {
val inputType = InputType.TYPE_CLASS_DATETIME or InputType.TYPE_DATETIME_VARIATION_DATE

assertTrue(GeneralKeyboardIME.shouldUseNumericKeyboard(inputType))
}

@Test
fun shouldUseNumericKeyboard_returnsTrueForPhoneInputs() {
assertTrue(GeneralKeyboardIME.shouldUseNumericKeyboard(InputType.TYPE_CLASS_PHONE))
}

@Test
fun shouldUseNumericKeyboard_returnsFalseForTextInputs() {
val inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS

assertFalse(GeneralKeyboardIME.shouldUseNumericKeyboard(inputType))
}

@Test
fun getKeyboardLayoutXMLForInputType_returnsNumericLayoutForNumberInputs() {
val inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL

assertEquals(
R.xml.keys_numeric,
GeneralKeyboardIME.getKeyboardLayoutXMLForInputType(inputType, R.xml.keys_letters_english),
)
}

@Test
fun getKeyboardLayoutXMLForInputType_returnsLetterLayoutForTextInputs() {
val inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS

assertEquals(
R.xml.keys_letters_english,
GeneralKeyboardIME.getKeyboardLayoutXMLForInputType(inputType, R.xml.keys_letters_english),
)
}
}
7 changes: 4 additions & 3 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#Tue Jan 04 09:48:27 CET 2022
#Sat May 09 08:20:13 PST 2026
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionSha256Sum=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading