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
80 changes: 80 additions & 0 deletions app/src/keyboards/java/be/scri/helpers/KeyboardStateManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package be.scri.helpers

import be.scri.models.ScribeState

/**
* Manages the current state transitions and state flags for the Scribe keyboard command system.
*/
class KeyboardStateManager {
/**
* The active state of the keyboard command system.
*/
var currentState: ScribeState = ScribeState.IDLE

/**
* Records the previous command source when an invalid state occurs.
*/
var invalidCommandSource: ScribeState = ScribeState.IDLE

/**
* Checks if the keyboard is currently in the `IDLE` state.
*/
val isIdle: Boolean
get() = currentState == ScribeState.IDLE

/**
* Checks if the keyboard is currently in the `SELECT_COMMAND` state.
*/
val isSelectCommand: Boolean
get() = currentState == ScribeState.SELECT_COMMAND

/**
* Checks if a command bar mode (translate, conjugate, plural, etc.) is active.
*/
val isCommandBarActive: Boolean
get() = currentState != ScribeState.IDLE && currentState != ScribeState.SELECT_COMMAND

/**
* Checks if the keyboard is currently in the `INVALID` state.
*/
val isInvalid: Boolean
get() = currentState == ScribeState.INVALID

/**
* Checks if the keyboard is currently in the `ALREADY_PLURAL` state.
*/
val isAlreadyPlural: Boolean
get() = currentState == ScribeState.ALREADY_PLURAL

/**
* Transitions the keyboard state to [newState].
*/
fun moveToState(newState: ScribeState) {
currentState = newState
}

/**
* Resets the keyboard state to `IDLE`.
*/
fun moveToIdle() {
currentState = ScribeState.IDLE
}

/**
* Transitions the keyboard to the `INVALID` state and records the [source] state.
*/
fun setInvalidState(source: ScribeState) {
invalidCommandSource = source
currentState = ScribeState.INVALID
}

/**
* Resets both [currentState] and [invalidCommandSource] to `IDLE`.
*/
fun reset() {
currentState = ScribeState.IDLE
invalidCommandSource = ScribeState.IDLE
}
}
47 changes: 28 additions & 19 deletions app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import be.scri.helpers.EmojiUtils.insertEmoji
import be.scri.helpers.FloatingKeyboardHandler
import be.scri.helpers.KeyboardBase
import be.scri.helpers.KeyboardLanguageMappingConstants
import be.scri.helpers.KeyboardStateManager
import be.scri.helpers.LanguageMappingConstants.getLanguageAlias
import be.scri.helpers.NativeSuggestionEngine
import be.scri.helpers.PreferencesHelper
Expand Down Expand Up @@ -187,8 +188,19 @@ abstract class GeneralKeyboardIME(
private var currentEnterKeyType: Int? = null
private var isNumericKeyboardActive: Boolean = false

internal var currentState: ScribeState = ScribeState.IDLE
internal var invalidCommandSource: ScribeState = ScribeState.IDLE
internal val stateManager = KeyboardStateManager()

internal var currentState: ScribeState
get() = stateManager.currentState
set(value) {
stateManager.currentState = value
}

internal var invalidCommandSource: ScribeState
get() = stateManager.invalidCommandSource
set(value) {
stateManager.invalidCommandSource = value
}

// Properties used by BackspaceHandler, delegated to UI Manager.
internal var currentCommandBarHint: String
Expand Down Expand Up @@ -793,7 +805,7 @@ abstract class GeneralKeyboardIME(
*/
internal fun moveToIdleState() {
clearSuggestionData()
currentState = ScribeState.IDLE
stateManager.moveToIdle()
saveConjugateModeType("none")
currentVerbForConjugation = null
selectedConjugationSubCategory = null
Expand All @@ -813,9 +825,9 @@ abstract class GeneralKeyboardIME(
// MARK: KeyboardUIListener

override fun onScribeKeyOptionsClicked() {
if (currentState == ScribeState.IDLE) {
if (stateManager.isIdle) {
clearSuggestionData()
currentState = ScribeState.SELECT_COMMAND
stateManager.moveToState(ScribeState.SELECT_COMMAND)
saveConjugateModeType("none")
currentVerbForConjugation = null
} else {
Expand All @@ -829,20 +841,20 @@ abstract class GeneralKeyboardIME(
}

override fun onTranslateClicked() {
currentState = ScribeState.TRANSLATE
stateManager.moveToState(ScribeState.TRANSLATE)
saveConjugateModeType("none")
refreshUI()
}

override fun onConjugateClicked() {
if (currentState != ScribeState.SELECT_VERB_CONJUNCTION) {
currentState = ScribeState.CONJUGATE
if (stateManager.currentState != ScribeState.SELECT_VERB_CONJUNCTION) {
stateManager.moveToState(ScribeState.CONJUGATE)
}
refreshUI()
}

override fun onPluralClicked() {
currentState = ScribeState.PLURAL
stateManager.moveToState(ScribeState.PLURAL)
saveConjugateModeType("none")
if (isPluralCapitalized) keyboard?.mShiftState = SHIFT_ON_ONE_CHAR
refreshUI()
Expand Down Expand Up @@ -992,8 +1004,7 @@ abstract class GeneralKeyboardIME(
}

if (commandModeOutput.isEmpty()) {
invalidCommandSource = currentState
currentState = ScribeState.INVALID
stateManager.setInvalidState(currentState)
refreshUI()
} else {
applyCommandOutput(commandModeOutput, inputConnection)
Expand Down Expand Up @@ -1027,14 +1038,12 @@ abstract class GeneralKeyboardIME(

conjugateLabels = dbManagers.conjugateDataManager.extractConjugateHeadings(dataContract, searchInput)

currentState =
if (conjugateOutput == null) {
invalidCommandSource = ScribeState.CONJUGATE
ScribeState.INVALID
} else {
saveConjugateModeType(language)
ScribeState.SELECT_VERB_CONJUNCTION
}
if (conjugateOutput == null) {
stateManager.setInvalidState(ScribeState.CONJUGATE)
} else {
saveConjugateModeType(language)
stateManager.moveToState(ScribeState.SELECT_VERB_CONJUNCTION)
}
refreshUI()
}

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

package be.scri.helpers

import be.scri.models.ScribeState
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test

class KeyboardStateManagerTest {
private lateinit var stateManager: KeyboardStateManager

@Before
fun setUp() {
stateManager = KeyboardStateManager()
}

@Test
fun initialState_isIdle() {
assertEquals(ScribeState.IDLE, stateManager.currentState)
assertEquals(ScribeState.IDLE, stateManager.invalidCommandSource)
assertTrue(stateManager.isIdle)
assertFalse(stateManager.isSelectCommand)
assertFalse(stateManager.isCommandBarActive)
assertFalse(stateManager.isInvalid)
assertFalse(stateManager.isAlreadyPlural)
}

@Test
fun moveToState_updatesCurrentState() {
stateManager.moveToState(ScribeState.TRANSLATE)
assertEquals(ScribeState.TRANSLATE, stateManager.currentState)
assertFalse(stateManager.isIdle)
assertTrue(stateManager.isCommandBarActive)

stateManager.moveToState(ScribeState.SELECT_COMMAND)
assertEquals(ScribeState.SELECT_COMMAND, stateManager.currentState)
assertTrue(stateManager.isSelectCommand)
assertFalse(stateManager.isCommandBarActive)
}

@Test
fun moveToIdle_resetsToIdle() {
stateManager.moveToState(ScribeState.CONJUGATE)
stateManager.moveToIdle()
assertEquals(ScribeState.IDLE, stateManager.currentState)
assertTrue(stateManager.isIdle)
}

@Test
fun setInvalidState_setsInvalidStateAndRecordsSource() {
stateManager.moveToState(ScribeState.PLURAL)
stateManager.setInvalidState(ScribeState.PLURAL)

assertEquals(ScribeState.INVALID, stateManager.currentState)
assertEquals(ScribeState.PLURAL, stateManager.invalidCommandSource)
assertTrue(stateManager.isInvalid)
}

@Test
fun isAlreadyPlural_returnsTrueWhenStateIsAlreadyPlural() {
stateManager.moveToState(ScribeState.ALREADY_PLURAL)
assertTrue(stateManager.isAlreadyPlural)
}

@Test
fun reset_resetsStateAndInvalidSourceToIdle() {
stateManager.setInvalidState(ScribeState.TRANSLATE)
stateManager.reset()

assertEquals(ScribeState.IDLE, stateManager.currentState)
assertEquals(ScribeState.IDLE, stateManager.invalidCommandSource)
assertTrue(stateManager.isIdle)
}
}
Loading