diff --git a/app/src/main/java/be/scri/helpers/EmojiUtils.kt b/app/src/main/java/be/scri/helpers/EmojiUtils.kt index 8b333b018..3ce0e42f9 100644 --- a/app/src/main/java/be/scri/helpers/EmojiUtils.kt +++ b/app/src/main/java/be/scri/helpers/EmojiUtils.kt @@ -10,6 +10,8 @@ import java.util.Locale object EmojiUtils { private const val DATA_SIZE_2 = 2 + val COMMON_EMOJIS = listOf("😀", "❤️", "👍", "😂", "🎉", "✨", "🔥", "👋", "😊") + /** * Checks if the end of a string is likely an emoji. * This is a heuristic check based on common emoji Unicode ranges. @@ -48,11 +50,22 @@ object EmojiUtils { ic: InputConnection, emojiKeywords: HashMap>?, emojiMaxKeywordLength: Int, + emojiColonModeOn: Boolean, ) { val maxLookBack = emojiMaxKeywordLength.coerceAtLeast(1) ic.beginBatchEdit() try { val prevText = ic.getTextBeforeCursor(maxLookBack, 0)?.toString() ?: "" + // If emoji colon suggestion is on, look back to the : that triggered emoji suggestions + // Delete that text, and add the emoji + if (emojiColonModeOn) { + val colonIndex = prevText.lastIndexOf(':') + if (colonIndex != -1) { + ic.deleteSurroundingText(prevText.length - colonIndex, 0) + } + ic.commitText(emoji, 1) + return + } val lastSpace = prevText.lastIndexOf(' ') when { prevText.isEmpty() || diff --git a/app/src/main/java/be/scri/helpers/KeyHandler.kt b/app/src/main/java/be/scri/helpers/KeyHandler.kt index 928e29790..20f907b6a 100644 --- a/app/src/main/java/be/scri/helpers/KeyHandler.kt +++ b/app/src/main/java/be/scri/helpers/KeyHandler.kt @@ -51,7 +51,7 @@ class KeyHandler( resetShiftIfNeeded(code) val previousWasLastKeySpace = wasLastKeySpace - if (code != KeyboardBase.KEYCODE_SPACE) { + if (code != KeyboardBase.KEYCODE_SPACE && !ime.emojiColonModeOn) { // None to clear in emoji colon mode, causes unnecessary flash when called suggestionHandler.clearLinguisticSuggestions() } @@ -134,6 +134,7 @@ class KeyHandler( /** * Handles the space key press and returns whether to reset wasLastKeySpace at the end. + * Switches emoji colon mode off, if on. * * @param previousWasLastKeySpace The previous state of wasLastKeySpace. * @@ -141,6 +142,11 @@ class KeyHandler( */ private fun handleSpaceKeyPress(previousWasLastKeySpace: Boolean): Boolean { wasLastKeySpace = spaceKeyProcessor.processKeycodeSpace(previousWasLastKeySpace) + // If we're suggesting emojis in colon mode, stop. Space should break emoji suggestions, and return to normal + if (ime.emojiColonModeOn) { + ime.emojiColonModeOn = false + ime.clearAutocomplete() + } return false } @@ -198,15 +204,27 @@ class KeyHandler( /** * Handles the delete/backspace key press. It delegates the deletion logic to the IME * and then triggers a re-evaluation of word suggestions based on the new text. + * Turns off emoji colon mode if colon is deleted that triggered it. */ private fun handleDeleteKey() { + val charToDelete = ime.currentInputConnection?.getTextBeforeCursor(1, 0) ime.handleDelete(ime.isDeleteRepeating()) // pass the actual repeating status if (ime.currentState == ScribeState.IDLE) { + val deletedChar = charToDelete?.takeIf { it.isNotEmpty() }?.last() + if (deletedChar == ':' && ime.emojiColonModeOn) { + ime.emojiColonModeOn = false + ime.clearAutocomplete() + } + val currentWord = ime.getLastWordBeforeCursor() - autocompletionHandler.processAutocomplete(currentWord) - suggestionHandler.processEmojiSuggestions(currentWord) + if (ime.emojiColonModeOn) { + suggestionHandler.processEmojiSuggestions(currentWord) + } else { + autocompletionHandler.processAutocomplete(currentWord) + suggestionHandler.processEmojiSuggestions(currentWord) + } } } @@ -243,10 +261,15 @@ class KeyHandler( /** * Handles the mode change key press (e.g., switching to the symbol keyboard). * It delegates the logic to the IME and clears any active suggestions. + * In emoji colon mode, restore emoji suggestions from before mode change. */ private fun handleModeChangeKey() { ime.handleModeChange(ime.keyboardMode, ime.keyboardView, ime) - suggestionHandler.clearAllSuggestionsAndHideButtonUI() + if (ime.emojiColonModeOn) { + suggestionHandler.processEmojiSuggestions(ime.getLastWordBeforeCursor()) + } else { + suggestionHandler.clearAllSuggestionsAndHideButtonUI() + } } /** @@ -341,7 +364,7 @@ class KeyHandler( /** * Handles default key presses (regular characters, numbers, symbols). * Commits the character to the input connection and processes suggestions. - * + * Toggles colon emoji mode on if ':' typed. * @param code The key code representing the character to input. */ @@ -358,9 +381,31 @@ class KeyHandler( ime.handleElseCondition(code, ime.keyboardMode, isCommandBarActive) if (ime.currentState == ScribeState.IDLE) { + if (code == ':'.code && ime.getLastWordBeforeCursor() == ":") { // " :" triggers emoji colon mode + ime.emojiColonModeOn = true + + val commonEmojis = EmojiUtils.COMMON_EMOJIS.toMutableList() + ime.autoSuggestEmojis = commonEmojis + ime.updateButtonVisibility(true) + ime.updateEmojiSuggestion(true, commonEmojis) // Show common emojis otherwise there's a small delay where words pop up + } + val currentWord = ime.getLastWordBeforeCursor() - autocompletionHandler.processAutocomplete(currentWord) - suggestionHandler.processEmojiSuggestions(currentWord) + if (ime.emojiColonModeOn) { + currentWord?.startsWith(":")?.let { + if (!it) { // Turn emoji colon mode off if there's no colon anymore (like if an emoji was just selected) + ime.emojiColonModeOn = false + ime.clearAutocomplete() + autocompletionHandler.processAutocomplete(currentWord) + suggestionHandler.processEmojiSuggestions(currentWord) + } else { + suggestionHandler.processEmojiSuggestions(currentWord) + } + } + } else { // Normal suggestions + autocompletionHandler.processAutocomplete(currentWord) + suggestionHandler.processEmojiSuggestions(currentWord) + } } else if (isCommandBarActive) { suggestionHandler.clearAllSuggestionsAndHideButtonUI() autocompletionHandler.clearAutocomplete() diff --git a/app/src/main/java/be/scri/helpers/SuggestionHandler.kt b/app/src/main/java/be/scri/helpers/SuggestionHandler.kt index 57b44deed..3f0fb1dcb 100644 --- a/app/src/main/java/be/scri/helpers/SuggestionHandler.kt +++ b/app/src/main/java/be/scri/helpers/SuggestionHandler.kt @@ -116,7 +116,6 @@ class SuggestionHandler( */ fun processEmojiSuggestions(currentWord: String?) { emojiSuggestionRunnable?.let { handler.removeCallbacks(it) } - emojiSuggestionRunnable = Runnable { if (ime.currentState != ScribeState.IDLE) { @@ -131,19 +130,30 @@ class SuggestionHandler( return@Runnable } - val emojis = - if (ime.emojiAutoSuggestionEnabled) { - ime.findEmojisForLastWord(ime.emojiKeywords, currentWord) - } else { - null - } + var emojis: MutableList? + if (ime.emojiColonModeOn) { + val currentWordCleaned = currentWord.removePrefix(":") // Drop colon that triggered emojiColonMode + emojis = + if ( + currentWordCleaned.isEmpty() + ) { // For no word typed yet, show common emojis + EmojiUtils.COMMON_EMOJIS.toMutableList() + } else { + ime.findEmojisForPrefix(ime.emojiKeywords, currentWordCleaned) + } + } else { + emojis = null + } val hasEmojiSuggestion = !emojis.isNullOrEmpty() if (hasEmojiSuggestion) { ime.autoSuggestEmojis = emojis - ime.updateEmojiSuggestion(true, emojis) ime.updateButtonVisibility(true) + ime.updateEmojiSuggestion(true, emojis) + } else if (ime.emojiColonModeOn) { // Show blank buttons when there are no matches + ime.autoSuggestEmojis = mutableListOf() + ime.updateEmojiSuggestion(true, mutableListOf()) } else { ime.updateButtonVisibility(false) } @@ -172,6 +182,7 @@ class SuggestionHandler( fun clearAllSuggestionsAndHideButtonUI() { emojiSuggestionRunnable?.let { handler.removeCallbacks(it) } linguisticSuggestionRunnable?.let { handler.removeCallbacks(it) } + wordSuggestionRunnable?.let { handler.removeCallbacks(it) } ime.disableAutoSuggest() diff --git a/app/src/main/java/be/scri/helpers/ui/KeyboardUIManager.kt b/app/src/main/java/be/scri/helpers/ui/KeyboardUIManager.kt index 928a94523..a0dda6db1 100644 --- a/app/src/main/java/be/scri/helpers/ui/KeyboardUIManager.kt +++ b/app/src/main/java/be/scri/helpers/ui/KeyboardUIManager.kt @@ -87,6 +87,33 @@ class KeyboardUIManager( var genderSuggestionLeft: Button? = binding.translateBtnLeft var genderSuggestionRight: Button? = binding.translateBtnRight + // 6-slot phone colon emoji row buttons + private val emojiColonPhoneButtons: List