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
37 changes: 29 additions & 8 deletions app/src/main/java/com/urik/keyboard/UrikInputMethodService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.urik.keyboard.service.EnglishPronounI
import com.urik.keyboard.service.InputMethod
import com.urik.keyboard.service.InputStateManager
import com.urik.keyboard.service.LanguageManager
import com.urik.keyboard.service.LastAutocorrection
import com.urik.keyboard.service.OutputBridge
import com.urik.keyboard.service.PostCommitReplacementState
import com.urik.keyboard.service.ProcessingResult
Expand Down Expand Up @@ -1150,6 +1151,7 @@ class UrikInputMethodService :
}

inputState.postCommitReplacementState = null
inputState.lastAutocorrection = null

inputState.isActivelyEditing = true

Expand Down Expand Up @@ -1272,6 +1274,7 @@ class UrikInputMethodService :
inputState.postCommitReplacementState = null
swipeKeyboardView?.clearSuggestions()
}
inputState.lastAutocorrection = null

if (inputState.spellConfirmationState == SpellConfirmationState.AWAITING_CONFIRMATION) {
outputBridge.beginBatchEdit()
Expand Down Expand Up @@ -2062,11 +2065,22 @@ class UrikInputMethodService :
inputState.displayBuffer = word
inputState.composingRegionStart = wordStart

suggestionPipeline.requestSuggestions(
buffer = word,
inputMethod = InputMethod.TYPED,
isCharacterInput = false
)
val autocorrection = inputState.lastAutocorrection
if (autocorrection != null &&
word.equals(autocorrection.correctedWord, ignoreCase = true)
) {
outputBridge.setComposingText(autocorrection.originalTypedWord, 1)
inputState.displayBuffer = autocorrection.originalTypedWord
inputState.pendingSuggestions = emptyList()
swipeKeyboardView?.clearSuggestions()
} else {
inputState.lastAutocorrection = null
suggestionPipeline.requestSuggestions(
buffer = word,
inputMethod = InputMethod.TYPED,
isCharacterInput = false
)
}
} else {
coordinateStateClear()
}
Expand Down Expand Up @@ -2192,7 +2206,10 @@ class UrikInputMethodService :
return@launch
}

if (currentSettings.autocorrectionEnabled && displaySuggestions.isNotEmpty()) {
if (currentSettings.autocorrectionEnabled &&
displaySuggestions.isNotEmpty() &&
inputState.lastAutocorrection == null
) {
val topSuggestion = displaySuggestions.first()
if (isSafeForAutocorrect(topSuggestion)) {
val originalWord = inputState.displayBuffer
Expand All @@ -2209,8 +2226,12 @@ class UrikInputMethodService :
originalWord = originalWord,
committedWord = topSuggestion
)
inputState.pendingSuggestions =
displaySuggestions.drop(1) + listOf(originalWord)
inputState.lastAutocorrection =
LastAutocorrection(
originalTypedWord = originalWord,
correctedWord = topSuggestion
)
inputState.pendingSuggestions = listOf(originalWord)
swipeKeyboardView?.updateSuggestions(inputState.pendingSuggestions)

val textBefore = outputBridge.safeGetTextBeforeCursor(50)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ enum class SpellConfirmationState {

data class PostCommitReplacementState(val originalWord: String, val committedWord: String)

data class LastAutocorrection(val originalTypedWord: String, val correctedWord: String)

interface ViewCallback {
fun clearSuggestions()

Expand Down Expand Up @@ -113,6 +115,10 @@ class InputStateManager(
var postCommitReplacementState: PostCommitReplacementState? = null
internal set

@Volatile
var lastAutocorrection: LastAutocorrection? = null
internal set

val selectionStateTracker = SelectionStateTracker()

val requiresDirectCommit: Boolean
Expand Down Expand Up @@ -170,6 +176,7 @@ class InputStateManager(
spellConfirmationState = SpellConfirmationState.NORMAL
pendingWordForLearning = null
postCommitReplacementState = null
lastAutocorrection = null
viewCallback.clearSuggestions()
composingRegionStart = -1
composingReassertionCount = 0
Expand Down Expand Up @@ -209,6 +216,7 @@ class InputStateManager(
spellConfirmationState = SpellConfirmationState.NORMAL
pendingWordForLearning = null
postCommitReplacementState = null
lastAutocorrection = null
viewCallback.clearSuggestions()
composingRegionStart = -1
lastKnownCursorPosition = -1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.urik.keyboard.service

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test

class InputStateManagerTest {
private var suggestionsCleared = false
private var lastSuggestions: List<String> = emptyList()

private lateinit var stateManager: InputStateManager

@Before
fun setup() {
suggestionsCleared = false
lastSuggestions = emptyList()

val viewCallback = object : ViewCallback {
override fun clearSuggestions() {
suggestionsCleared = true
}

override fun updateSuggestions(suggestions: List<String>) {
lastSuggestions = suggestions
}
}

stateManager = InputStateManager(
viewCallback = viewCallback,
onShiftStateChanged = {},
isCapsLockOn = { false },
cancelDebounceJob = {}
)
}

@Test
fun `lastAutocorrection persists independently of postCommitReplacementState`() {
stateManager.lastAutocorrection = LastAutocorrection("teh", "the")
stateManager.postCommitReplacementState = PostCommitReplacementState("teh", "the")

stateManager.postCommitReplacementState = null

assertNotNull(stateManager.lastAutocorrection)
assertEquals("teh", stateManager.lastAutocorrection?.originalTypedWord)
assertEquals("the", stateManager.lastAutocorrection?.correctedWord)
}

@Test
fun `clearInternalStateOnly clears lastAutocorrection`() {
stateManager.lastAutocorrection = LastAutocorrection("teh", "the")

stateManager.clearInternalStateOnly()

assertNull(stateManager.lastAutocorrection)
}

@Test
fun `invalidateComposingState clears lastAutocorrection`() {
stateManager.lastAutocorrection = LastAutocorrection("teh", "the")

stateManager.invalidateComposingState()

assertNull(stateManager.lastAutocorrection)
}

@Test
fun `clearInternalStateOnly clears postCommitReplacementState`() {
stateManager.postCommitReplacementState = PostCommitReplacementState("teh", "the")

stateManager.clearInternalStateOnly()

assertNull(stateManager.postCommitReplacementState)
}

@Test
fun `clearBigramPredictions does not affect lastAutocorrection`() {
stateManager.lastAutocorrection = LastAutocorrection("teh", "the")
stateManager.isShowingBigramPredictions = true

stateManager.clearBigramPredictions()

assertNotNull(stateManager.lastAutocorrection)
}

@Test
fun `clearSpellConfirmationFields does not affect lastAutocorrection`() {
stateManager.lastAutocorrection = LastAutocorrection("teh", "the")
stateManager.spellConfirmationState = SpellConfirmationState.AWAITING_CONFIRMATION

stateManager.clearSpellConfirmationFields()

assertNotNull(stateManager.lastAutocorrection)
}
}
Loading