From 91ffa5d3c6853e9b06ef0fca1f7981811e6975fa Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Mon, 6 Jul 2026 18:11:13 +0530 Subject: [PATCH 01/10] feat: highlight autocomplete suggestion when it matches typed word Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../be/scri/helpers/AutocompletionHandler.kt | 2 +- .../be/scri/services/GeneralKeyboardIME.kt | 29 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt b/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt index b8475281..d1edb4cc 100644 --- a/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt +++ b/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt @@ -49,7 +49,7 @@ class AutocompletionHandler( val completions = ime.getAutocompletions(currentWord, limit = 5) if (completions.isNotEmpty()) { - ime.updateAutocompleteSuggestions(completions) + ime.updateAutocompleteSuggestions(completions, currentWord) } else { ime.clearAutocomplete() } diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index 4957ac9f..e94047b8 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -8,6 +8,7 @@ import android.content.Context import android.content.Intent import android.database.sqlite.SQLiteException import android.graphics.Color +import android.graphics.Typeface import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.graphics.drawable.RippleDrawable @@ -1768,9 +1769,17 @@ abstract class GeneralKeyboardIME( private fun setSuggestionButton( button: Button, text: String, + isExactMatch: Boolean = false, ) { val isUserDarkMode = getIsDarkModeOrNot(applicationContext) - val textColor = if (isUserDarkMode) Color.WHITE else "#1E1E1E".toColorInt() + val textColor = + if (isExactMatch) { + ContextCompat.getColor(applicationContext, R.color.theme_scribe_blue) + } else if (isUserDarkMode) { + Color.WHITE + } else { + "#1E1E1E".toColorInt() + } button.text = text button.isAllCaps = false button.visibility = View.VISIBLE @@ -1778,6 +1787,7 @@ abstract class GeneralKeyboardIME( button.setOnClickListener(null) button.background = null button.setTextColor(textColor) + button.setTypeface(button.typeface, if (isExactMatch) Typeface.BOLD else Typeface.NORMAL) button.setOnClickListener { currentInputConnection?.commitText("$text ", 1) moveToIdleState() @@ -1790,7 +1800,10 @@ abstract class GeneralKeyboardIME( * Updates autocomplete UI with a new list of suggestions. * Clears it if not idle or no completions. */ - fun updateAutocompleteSuggestions(completions: List?) { + fun updateAutocompleteSuggestions( + completions: List?, + currentWord: String? = null, + ) { if (currentState != ScribeState.IDLE) { uiManager.disableAutoSuggest(language) return @@ -1804,9 +1817,9 @@ abstract class GeneralKeyboardIME( val completion2 = completions.getOrNull(1) ?: "" val completion3 = completions.getOrNull(2) ?: "" - setAutocompleteButton(uiManager.binding.conjugateBtn, completion1) - setAutocompleteButton(uiManager.binding.translateBtn, completion2) - setAutocompleteButton(uiManager.pluralBtn!!, completion3) + setAutocompleteButton(uiManager.binding.conjugateBtn, completion1, currentWord) + setAutocompleteButton(uiManager.binding.translateBtn, completion2, currentWord) + setAutocompleteButton(uiManager.pluralBtn!!, completion3, currentWord) uiManager.binding.separator1.visibility = View.VISIBLE uiManager.binding.separator2.visibility = View.VISIBLE @@ -1815,12 +1828,16 @@ abstract class GeneralKeyboardIME( /** * Sets up an autocomplete button with the given suggestion text. * When clicked, it replaces the current word with the suggestion. + * If the suggestion exactly matches what the user has already typed, + * the button is highlighted to signal that pressing it is a no-op. */ private fun setAutocompleteButton( button: Button, text: String, + currentWord: String? = null, ) { - setSuggestionButton(button, text) + val isExactMatch = text.isNotBlank() && text.equals(currentWord, ignoreCase = true) + setSuggestionButton(button, text, isExactMatch) if (text.isBlank()) { button.setOnClickListener(null) return From 43c4032aac16f6d6d9b0a66f1fa630d93f93269e Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Tue, 7 Jul 2026 03:32:55 +0530 Subject: [PATCH 02/10] fix: surface exact-typed word as a completion so it can be highlighted Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../be/scri/services/GeneralKeyboardIME.kt | 35 +++++++++++++++---- .../be/scri/helpers/NativeSuggestionEngine.kt | 16 +++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index e94047b8..155c401a 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -1071,13 +1071,37 @@ abstract class GeneralKeyboardIME( prefix: String, limit: Int = 3, ): List { - if (this::nativeSuggestionEngine.isInitialized) { - val nativeCompletions = nativeSuggestionEngine.getAutocompletions(language, prefix, limit) - if (nativeCompletions.isNotEmpty()) { - return nativeCompletions + val completions = + if (this::nativeSuggestionEngine.isInitialized) { + val nativeCompletions = nativeSuggestionEngine.getAutocompletions(language, prefix, limit) + if (nativeCompletions.isNotEmpty()) { + nativeCompletions + } else { + getFallbackAutocompletions(prefix, limit) + } + } else { + getFallbackAutocompletions(prefix, limit) } + + // The native dictionary only returns completions that extend `prefix`, never `prefix` + // itself. If what the user has already typed is itself a valid word, surface it as the + // first suggestion so it can be highlighted, rather than relying on it happening to be + // among the completions above. + val isPrefixItselfAValidWord = + this::nativeSuggestionEngine.isInitialized && nativeSuggestionEngine.isValidWord(language, prefix) + + return if (isPrefixItselfAValidWord && completions.none { it.equals(prefix, ignoreCase = true) }) { + (listOf(prefix) + completions).take(limit) + } else { + completions } - return try { + } + + private fun getFallbackAutocompletions( + prefix: String, + limit: Int, + ): List = + try { dbManagers.autocompletionManager.getAutocompletions(prefix, limit) } catch (e: SQLiteException) { Log.e("GeneralKeyboardIME", "Database error in autocompletion", e) @@ -1086,7 +1110,6 @@ abstract class GeneralKeyboardIME( Log.e("GeneralKeyboardIME", "Illegal state in autocompletion", e) emptyList() } - } /** * Gets the current text in the command bar without the cursor. diff --git a/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt b/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt index cfb3778c..05943f72 100644 --- a/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt +++ b/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt @@ -143,6 +143,22 @@ class NativeSuggestionEngine(private val context: Context) { } } + /** + * Checks whether the given word is itself a complete, valid word in the native dictionary, + * independent of whatever completions [getAutocompletions] returns for it. + */ + fun isValidWord(language: String, word: String): Boolean { + val dict = getDictionary(language) ?: return false + if (word.isBlank()) return false + + return try { + dict.isValidWord(word) || dict.isValidWord(word.lowercase(Locale.ROOT)) + } catch (e: Exception) { + Log.e(TAG, "Error checking dictionary validity for $word", e) + false + } + } + /** * Queries the native dictionary engine for next-word suggestions (bigram/trigram predictions) given the last typed word. */ From 87596bd4f635d2223383270818e36f3a025eacad Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Wed, 8 Jul 2026 23:52:38 +0530 Subject: [PATCH 03/10] feat: highlight autocomplete suggestion with a background instead of blue text Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../be/scri/services/GeneralKeyboardIME.kt | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index 155c401a..de73687e 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -9,7 +9,9 @@ import android.content.Intent import android.database.sqlite.SQLiteException import android.graphics.Color import android.graphics.Typeface +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.InsetDrawable import android.graphics.drawable.LayerDrawable import android.graphics.drawable.RippleDrawable import android.inputmethodservice.InputMethodService @@ -183,6 +185,9 @@ abstract class GeneralKeyboardIME( const val TEXT_LENGTH = 20 const val NOUN_TYPE_SIZE = 20f const val SUGGESTION_SIZE = 15f + const val SUGGESTION_HIGHLIGHT_ALPHA = 51 + const val SUGGESTION_HIGHLIGHT_CORNER_RADIUS_DP = 8f + const val SUGGESTION_HIGHLIGHT_INSET_DP = 4f const val DARK_THEME = "#aeb3be" const val LIGHT_THEME = "#4b4b4b" internal const val MAX_TEXT_LENGTH = 1000 @@ -1792,31 +1797,44 @@ abstract class GeneralKeyboardIME( private fun setSuggestionButton( button: Button, text: String, - isExactMatch: Boolean = false, + isHighlighted: Boolean = false, ) { val isUserDarkMode = getIsDarkModeOrNot(applicationContext) - val textColor = - if (isExactMatch) { - ContextCompat.getColor(applicationContext, R.color.theme_scribe_blue) - } else if (isUserDarkMode) { - Color.WHITE - } else { - "#1E1E1E".toColorInt() - } + val textColor = if (isUserDarkMode) Color.WHITE else "#1E1E1E".toColorInt() button.text = text button.isAllCaps = false button.visibility = View.VISIBLE button.textSize = SUGGESTION_SIZE button.setOnClickListener(null) - button.background = null + button.background = if (isHighlighted) buildSuggestionHighlightBackground() else null button.setTextColor(textColor) - button.setTypeface(button.typeface, if (isExactMatch) Typeface.BOLD else Typeface.NORMAL) + button.setTypeface(button.typeface, Typeface.NORMAL) button.setOnClickListener { currentInputConnection?.commitText("$text ", 1) moveToIdleState() } } + /** + * Builds the rounded, tinted background used to highlight a suggestion chip that the + * keyboard considers "obvious" (e.g. the user has already typed it in full). + */ + private fun buildSuggestionHighlightBackground(): Drawable { + val highlightColor = + ColorUtils.setAlphaComponent( + ContextCompat.getColor(applicationContext, R.color.theme_scribe_blue), + SUGGESTION_HIGHLIGHT_ALPHA, + ) + val background = + GradientDrawable().apply { + shape = GradientDrawable.RECTANGLE + cornerRadius = SUGGESTION_HIGHLIGHT_CORNER_RADIUS_DP * resources.displayMetrics.density + setColor(highlightColor) + } + val insetPx = (SUGGESTION_HIGHLIGHT_INSET_DP * resources.displayMetrics.density).toInt() + return InsetDrawable(background, insetPx, insetPx, insetPx, insetPx) + } + // MARK: Autocomplete /** @@ -1860,7 +1878,7 @@ abstract class GeneralKeyboardIME( currentWord: String? = null, ) { val isExactMatch = text.isNotBlank() && text.equals(currentWord, ignoreCase = true) - setSuggestionButton(button, text, isExactMatch) + setSuggestionButton(button, text, isHighlighted = isExactMatch) if (text.isBlank()) { button.setOnClickListener(null) return From 2f653bf4e64722515cf910b9474e5a779d5edea7 Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Wed, 8 Jul 2026 23:56:42 +0530 Subject: [PATCH 04/10] feat: insert highlighted autocomplete suggestion on space press Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../java/be/scri/helpers/SpaceKeyProcessor.kt | 7 +++ .../be/scri/services/GeneralKeyboardIME.kt | 47 ++++++++++++++++--- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/app/src/keyboards/java/be/scri/helpers/SpaceKeyProcessor.kt b/app/src/keyboards/java/be/scri/helpers/SpaceKeyProcessor.kt index 568975a8..af85b314 100644 --- a/app/src/keyboards/java/be/scri/helpers/SpaceKeyProcessor.kt +++ b/app/src/keyboards/java/be/scri/helpers/SpaceKeyProcessor.kt @@ -61,6 +61,13 @@ class SpaceKeyProcessor( * @param wasLastKeySpace true if the previous key pressed was a space. */ private fun handleSpaceOutsideCommandBar(wasLastKeySpace: Boolean) { + if (ime.tryInsertHighlightedAutocompleteSuggestion()) { + val wordBeforeSpace = ime.getLastWordBeforeCursor() + suggestionHandler.processLinguisticSuggestions(wordBeforeSpace) + suggestionHandler.processWordSuggestions(wordBeforeSpace) + return + } + val periodOnDoubleTapEnabled = PreferencesHelper.getEnablePeriodOnSpaceBarDoubleTap(context = ime, ime.language) val ic = ime.currentInputConnection ?: return val wordBeforeSpace = ime.getLastWordBeforeCursor() diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index de73687e..210873f2 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -159,6 +159,12 @@ abstract class GeneralKeyboardIME( private var currentEnterKeyType: Int? = null private var isNumericKeyboardActive: Boolean = false + /** + * The autocomplete suggestion currently shown highlighted (if any), so a following space + * press can insert it in place of what the user has typed, matching Scribe-iOS behavior. + */ + private var highlightedAutocompleteSuggestion: String? = null + internal var currentState: ScribeState = ScribeState.IDLE internal var invalidCommandSource: ScribeState = ScribeState.IDLE @@ -1846,10 +1852,12 @@ abstract class GeneralKeyboardIME( currentWord: String? = null, ) { if (currentState != ScribeState.IDLE) { + highlightedAutocompleteSuggestion = null uiManager.disableAutoSuggest(language) return } if (completions.isNullOrEmpty()) { + highlightedAutocompleteSuggestion = null uiManager.disableAutoSuggest(language) return } @@ -1858,6 +1866,9 @@ abstract class GeneralKeyboardIME( val completion2 = completions.getOrNull(1) ?: "" val completion3 = completions.getOrNull(2) ?: "" + highlightedAutocompleteSuggestion = + completion1.takeIf { it.isNotBlank() && it.equals(currentWord, ignoreCase = true) } + setAutocompleteButton(uiManager.binding.conjugateBtn, completion1, currentWord) setAutocompleteButton(uiManager.binding.translateBtn, completion2, currentWord) setAutocompleteButton(uiManager.pluralBtn!!, completion3, currentWord) @@ -1884,21 +1895,45 @@ abstract class GeneralKeyboardIME( return } button.setOnClickListener { - val ic = currentInputConnection ?: return@setOnClickListener - val beforeText = ic.getTextBeforeCursor(50, 0) ?: "" - val wordStartIndex = beforeText.lastIndexOfAny(charArrayOf(' ', '\n', '\t', '.', ',', '?', '!')) + 1 - val currentWord = beforeText.substring(wordStartIndex) - ic.deleteSurroundingText(currentWord.length, 0) - ic.commitText(text, 1) + replaceCurrentWordWithSuggestion(text) moveToIdleState() } } + /** + * Deletes the word currently being typed (immediately before the cursor) and commits + * [text] in its place. + */ + private fun replaceCurrentWordWithSuggestion(text: String) { + val ic = currentInputConnection ?: return + val beforeText = ic.getTextBeforeCursor(50, 0) ?: "" + val wordStartIndex = beforeText.lastIndexOfAny(charArrayOf(' ', '\n', '\t', '.', ',', '?', '!')) + 1 + val currentWord = beforeText.substring(wordStartIndex) + ic.deleteSurroundingText(currentWord.length, 0) + ic.commitText(text, 1) + } + + /** + * If an autocomplete suggestion is currently highlighted (i.e. the user has typed a word + * that's "obviously" what they want), replaces the typed word with it, appends a space, and + * returns true. Otherwise does nothing and returns false. Lets the space key complete an + * obvious word instead of just inserting a literal space, matching Scribe-iOS behavior. + */ + fun tryInsertHighlightedAutocompleteSuggestion(): Boolean { + val suggestion = highlightedAutocompleteSuggestion ?: return false + highlightedAutocompleteSuggestion = null + replaceCurrentWordWithSuggestion(suggestion) + currentInputConnection?.commitText(" ", 1) + moveToIdleState() + return true + } + /** * Clears autocomplete suggestions by resetting the suggestion strip * to the default command buttons via the UI Manager. */ fun clearAutocomplete() { + highlightedAutocompleteSuggestion = null if (this::uiManager.isInitialized) { uiManager.disableAutoSuggest(language) } From 568ab4124cc290e91d8e77105cfdf83307889069 Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Thu, 9 Jul 2026 00:01:42 +0530 Subject: [PATCH 05/10] feat: highlight the single unambiguous autocomplete candidate Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../be/scri/helpers/AutocompletionHandler.kt | 6 +-- .../be/scri/services/GeneralKeyboardIME.kt | 51 ++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt b/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt index d1edb4cc..98c80827 100644 --- a/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt +++ b/app/src/keyboards/java/be/scri/helpers/AutocompletionHandler.kt @@ -46,10 +46,10 @@ class AutocompletionHandler( return@Runnable } - val completions = ime.getAutocompletions(currentWord, limit = 5) + val result = ime.getAutocompletions(currentWord, limit = 5) - if (completions.isNotEmpty()) { - ime.updateAutocompleteSuggestions(completions, currentWord) + if (result.completions.isNotEmpty()) { + ime.updateAutocompleteSuggestions(result.completions, result.highlightedSuggestion) } else { ime.clearAutocomplete() } diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index 210873f2..18c1aed2 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -1074,14 +1074,23 @@ abstract class GeneralKeyboardIME( // MARK: State & Logic Helpers + /** + * The result of an autocomplete lookup: the ordered suggestions to display, and which one + * (if any) is "obvious" enough to highlight and auto-insert on space. + */ + data class AutocompleteResult( + val completions: List, + val highlightedSuggestion: String?, + ) + /** * Safely fetches autocomplete suggestions for the given prefix. - * Returns an empty list if a database or state error occurs. + * Returns an empty result if a database or state error occurs. */ fun getAutocompletions( prefix: String, limit: Int = 3, - ): List { + ): AutocompleteResult { val completions = if (this::nativeSuggestionEngine.isInitialized) { val nativeCompletions = nativeSuggestionEngine.getAutocompletions(language, prefix, limit) @@ -1101,11 +1110,19 @@ abstract class GeneralKeyboardIME( val isPrefixItselfAValidWord = this::nativeSuggestionEngine.isInitialized && nativeSuggestionEngine.isValidWord(language, prefix) - return if (isPrefixItselfAValidWord && completions.none { it.equals(prefix, ignoreCase = true) }) { - (listOf(prefix) + completions).take(limit) - } else { - completions + if (isPrefixItselfAValidWord && completions.none { it.equals(prefix, ignoreCase = true) }) { + return AutocompleteResult((listOf(prefix) + completions).take(limit), highlightedSuggestion = prefix) } + + // If exactly one word could possibly complete what's been typed, it's unambiguous: highlight + // it as the first suggestion, but still offer the literally typed prefix as another option, + // in case the user actually wanted to keep typing their own word. + if (!isPrefixItselfAValidWord && completions.size == 1) { + val onlyCompletion = completions.first() + return AutocompleteResult(listOf(onlyCompletion, prefix).take(limit), highlightedSuggestion = onlyCompletion) + } + + return AutocompleteResult(completions, highlightedSuggestion = null) } private fun getFallbackAutocompletions( @@ -1849,7 +1866,7 @@ abstract class GeneralKeyboardIME( */ fun updateAutocompleteSuggestions( completions: List?, - currentWord: String? = null, + highlightedSuggestion: String? = null, ) { if (currentState != ScribeState.IDLE) { highlightedAutocompleteSuggestion = null @@ -1866,12 +1883,11 @@ abstract class GeneralKeyboardIME( val completion2 = completions.getOrNull(1) ?: "" val completion3 = completions.getOrNull(2) ?: "" - highlightedAutocompleteSuggestion = - completion1.takeIf { it.isNotBlank() && it.equals(currentWord, ignoreCase = true) } + highlightedAutocompleteSuggestion = highlightedSuggestion - setAutocompleteButton(uiManager.binding.conjugateBtn, completion1, currentWord) - setAutocompleteButton(uiManager.binding.translateBtn, completion2, currentWord) - setAutocompleteButton(uiManager.pluralBtn!!, completion3, currentWord) + setAutocompleteButton(uiManager.binding.conjugateBtn, completion1, highlightedSuggestion) + setAutocompleteButton(uiManager.binding.translateBtn, completion2, highlightedSuggestion) + setAutocompleteButton(uiManager.pluralBtn!!, completion3, highlightedSuggestion) uiManager.binding.separator1.visibility = View.VISIBLE uiManager.binding.separator2.visibility = View.VISIBLE @@ -1880,16 +1896,17 @@ abstract class GeneralKeyboardIME( /** * Sets up an autocomplete button with the given suggestion text. * When clicked, it replaces the current word with the suggestion. - * If the suggestion exactly matches what the user has already typed, - * the button is highlighted to signal that pressing it is a no-op. + * If this suggestion is the one the keyboard considers "obvious" (whether because the user + * typed it in full, or because it's the only word that can follow what's been typed), the + * button is highlighted to signal that pressing space will also insert it. */ private fun setAutocompleteButton( button: Button, text: String, - currentWord: String? = null, + highlightedSuggestion: String? = null, ) { - val isExactMatch = text.isNotBlank() && text.equals(currentWord, ignoreCase = true) - setSuggestionButton(button, text, isHighlighted = isExactMatch) + val isHighlighted = text.isNotBlank() && text.equals(highlightedSuggestion, ignoreCase = true) + setSuggestionButton(button, text, isHighlighted) if (text.isBlank()) { button.setOnClickListener(null) return From 125cf3f029d01211575eba49ee16e0247c061a4e Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Thu, 9 Jul 2026 17:06:24 +0530 Subject: [PATCH 06/10] fix: place highlighted autocomplete suggestion in the first visual slot Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../keyboards/java/be/scri/services/GeneralKeyboardIME.kt | 7 +++++-- .../main/java/be/scri/helpers/NativeSuggestionEngine.kt | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index 18c1aed2..93eae2e4 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -1885,8 +1885,11 @@ abstract class GeneralKeyboardIME( highlightedAutocompleteSuggestion = highlightedSuggestion - setAutocompleteButton(uiManager.binding.conjugateBtn, completion1, highlightedSuggestion) - setAutocompleteButton(uiManager.binding.translateBtn, completion2, highlightedSuggestion) + // Visual left-to-right order of these buttons is translateBtn, conjugateBtn, pluralBtn + // (see input_method_view.xml's constraint chain), so completion1 -- the one that may be + // highlighted -- must go in translateBtn to actually land in the first, leftmost slot. + setAutocompleteButton(uiManager.binding.translateBtn, completion1, highlightedSuggestion) + setAutocompleteButton(uiManager.binding.conjugateBtn, completion2, highlightedSuggestion) setAutocompleteButton(uiManager.pluralBtn!!, completion3, highlightedSuggestion) uiManager.binding.separator1.visibility = View.VISIBLE diff --git a/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt b/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt index 05943f72..20f6478c 100644 --- a/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt +++ b/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt @@ -152,7 +152,9 @@ class NativeSuggestionEngine(private val context: Context) { if (word.isBlank()) return false return try { - dict.isValidWord(word) || dict.isValidWord(word.lowercase(Locale.ROOT)) + dict.isValidWord(word) || + dict.isValidWord(word.lowercase(Locale.ROOT)) || + dict.isValidWord(word.replaceFirstChar { it.uppercaseChar() }) } catch (e: Exception) { Log.e(TAG, "Error checking dictionary validity for $word", e) false From 62e12910938138384e4003773444f796b9461739 Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Thu, 9 Jul 2026 17:30:09 +0530 Subject: [PATCH 07/10] feat: add colored underline to autocomplete suggestions Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../be/scri/services/GeneralKeyboardIME.kt | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index 93eae2e4..5ed258cc 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -22,6 +22,7 @@ import android.text.InputType.TYPE_CLASS_NUMBER import android.text.InputType.TYPE_CLASS_PHONE import android.text.InputType.TYPE_MASK_CLASS import android.util.Log +import android.view.Gravity import android.view.KeyEvent import android.view.View import android.view.inputmethod.EditorInfo @@ -194,6 +195,7 @@ abstract class GeneralKeyboardIME( const val SUGGESTION_HIGHLIGHT_ALPHA = 51 const val SUGGESTION_HIGHLIGHT_CORNER_RADIUS_DP = 8f const val SUGGESTION_HIGHLIGHT_INSET_DP = 4f + const val SUGGESTION_UNDERLINE_HEIGHT_DP = 3f const val DARK_THEME = "#aeb3be" const val LIGHT_THEME = "#4b4b4b" internal const val MAX_TEXT_LENGTH = 1000 @@ -1821,6 +1823,7 @@ abstract class GeneralKeyboardIME( button: Button, text: String, isHighlighted: Boolean = false, + showUnderline: Boolean = false, ) { val isUserDarkMode = getIsDarkModeOrNot(applicationContext) val textColor = if (isUserDarkMode) Color.WHITE else "#1E1E1E".toColorInt() @@ -1829,7 +1832,7 @@ abstract class GeneralKeyboardIME( button.visibility = View.VISIBLE button.textSize = SUGGESTION_SIZE button.setOnClickListener(null) - button.background = if (isHighlighted) buildSuggestionHighlightBackground() else null + button.background = if (showUnderline && text.isNotBlank()) buildSuggestionBackground(isHighlighted) else null button.setTextColor(textColor) button.setTypeface(button.typeface, Typeface.NORMAL) button.setOnClickListener { @@ -1839,23 +1842,47 @@ abstract class GeneralKeyboardIME( } /** - * Builds the rounded, tinted background used to highlight a suggestion chip that the - * keyboard considers "obvious" (e.g. the user has already typed it in full). + * Builds the background for an autocomplete suggestion chip: a colored underline (green when + * the suggestion is highlighted as "obvious", red otherwise), plus a rounded, tinted fill + * behind the text when highlighted -- matching the Scribe-iOS reference design. */ - private fun buildSuggestionHighlightBackground(): Drawable { + private fun buildSuggestionBackground(isHighlighted: Boolean): Drawable { + val density = resources.displayMetrics.density + val insetPx = (SUGGESTION_HIGHLIGHT_INSET_DP * density).toInt() + val underlineHeightPx = (SUGGESTION_UNDERLINE_HEIGHT_DP * density).toInt() + val underlineColorRes = if (isHighlighted) R.color.annotateGreen else R.color.annotateRed + val underline = + GradientDrawable().apply { + shape = GradientDrawable.RECTANGLE + cornerRadius = underlineHeightPx / 2f + setColor(ContextCompat.getColor(applicationContext, underlineColorRes)) + } + + val layers = if (isHighlighted) arrayOf(buildSuggestionHighlightFill(), underline) else arrayOf(underline) + val underlineIndex = layers.lastIndex + val layered = + LayerDrawable(layers).apply { + setLayerGravity(underlineIndex, Gravity.BOTTOM) + setLayerHeight(underlineIndex, underlineHeightPx) + } + return InsetDrawable(layered, insetPx, insetPx, insetPx, insetPx) + } + + /** + * Builds the rounded, tinted fill used to highlight a suggestion chip that the keyboard + * considers "obvious" (e.g. the user has already typed it in full). + */ + private fun buildSuggestionHighlightFill(): Drawable { val highlightColor = ColorUtils.setAlphaComponent( ContextCompat.getColor(applicationContext, R.color.theme_scribe_blue), SUGGESTION_HIGHLIGHT_ALPHA, ) - val background = - GradientDrawable().apply { - shape = GradientDrawable.RECTANGLE - cornerRadius = SUGGESTION_HIGHLIGHT_CORNER_RADIUS_DP * resources.displayMetrics.density - setColor(highlightColor) - } - val insetPx = (SUGGESTION_HIGHLIGHT_INSET_DP * resources.displayMetrics.density).toInt() - return InsetDrawable(background, insetPx, insetPx, insetPx, insetPx) + return GradientDrawable().apply { + shape = GradientDrawable.RECTANGLE + cornerRadius = SUGGESTION_HIGHLIGHT_CORNER_RADIUS_DP * resources.displayMetrics.density + setColor(highlightColor) + } } // MARK: Autocomplete @@ -1909,7 +1936,7 @@ abstract class GeneralKeyboardIME( highlightedSuggestion: String? = null, ) { val isHighlighted = text.isNotBlank() && text.equals(highlightedSuggestion, ignoreCase = true) - setSuggestionButton(button, text, isHighlighted) + setSuggestionButton(button, text, isHighlighted, showUnderline = true) if (text.isBlank()) { button.setOnClickListener(null) return From 48e7fae901abd7d501777e44a84f3b45551987d2 Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Thu, 9 Jul 2026 18:18:52 +0530 Subject: [PATCH 08/10] fix: require autocomplete suggestions to start with the typed prefix Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../main/java/be/scri/helpers/NativeSuggestionEngine.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt b/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt index 20f6478c..a1e9eecc 100644 --- a/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt +++ b/app/src/main/java/be/scri/helpers/NativeSuggestionEngine.kt @@ -134,8 +134,11 @@ class NativeSuggestionEngine(private val context: Context) { ) suggestions?.map { it.mWord } - ?.filter { it.isNotBlank() && it.lowercase(Locale.ROOT) != prefix.lowercase(Locale.ROOT) } - ?.take(limit) + ?.filter { + it.isNotBlank() && + it.lowercase(Locale.ROOT) != prefix.lowercase(Locale.ROOT) && + it.startsWith(prefix, ignoreCase = true) + }?.take(limit) ?: emptyList() } catch (e: Exception) { Log.e(TAG, "Error fetching native suggestions for $prefix", e) From def6e05184860b261e2aef3e7d3ceaf7e2646dba Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Sun, 12 Jul 2026 02:29:23 +0530 Subject: [PATCH 09/10] fix: prefer deterministic prefix lookup and alphabetical order for autocomplete Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../be/scri/services/GeneralKeyboardIME.kt | 35 ++++++++++++------- .../main/java/be/scri/helpers/data/Trie.kt | 4 +-- app/src/main/res/values-night/colors.xml | 2 ++ app/src/main/res/values/colors.xml | 2 ++ 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index 5ed258cc..7aa288a9 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -193,9 +193,11 @@ abstract class GeneralKeyboardIME( const val NOUN_TYPE_SIZE = 20f const val SUGGESTION_SIZE = 15f const val SUGGESTION_HIGHLIGHT_ALPHA = 51 - const val SUGGESTION_HIGHLIGHT_CORNER_RADIUS_DP = 8f - const val SUGGESTION_HIGHLIGHT_INSET_DP = 4f + const val SUGGESTION_HIGHLIGHT_CORNER_RADIUS_DP = 12f + const val SUGGESTION_HIGHLIGHT_HORIZONTAL_INSET_DP = 4f + const val SUGGESTION_HIGHLIGHT_VERTICAL_INSET_DP = 1f const val SUGGESTION_UNDERLINE_HEIGHT_DP = 3f + const val SUGGESTION_UNDERLINE_EXTRA_HORIZONTAL_INSET_DP = 6f const val DARK_THEME = "#aeb3be" const val LIGHT_THEME = "#4b4b4b" internal const val MAX_TEXT_LENGTH = 1000 @@ -1093,16 +1095,17 @@ abstract class GeneralKeyboardIME( prefix: String, limit: Int = 3, ): AutocompleteResult { + // The SQL/Trie-backed lexicon is a deterministic, pure prefix match and is preferred. + // The native dictionary engine's suggestions can drift from the typed prefix on longer + // words (see PR discussion), so it's only used when the lexicon has no data for this + // language/prefix yet. val completions = - if (this::nativeSuggestionEngine.isInitialized) { - val nativeCompletions = nativeSuggestionEngine.getAutocompletions(language, prefix, limit) - if (nativeCompletions.isNotEmpty()) { - nativeCompletions + getDeterministicAutocompletions(prefix, limit).ifEmpty { + if (this::nativeSuggestionEngine.isInitialized) { + nativeSuggestionEngine.getAutocompletions(language, prefix, limit) } else { - getFallbackAutocompletions(prefix, limit) + emptyList() } - } else { - getFallbackAutocompletions(prefix, limit) } // The native dictionary only returns completions that extend `prefix`, never `prefix` @@ -1127,7 +1130,7 @@ abstract class GeneralKeyboardIME( return AutocompleteResult(completions, highlightedSuggestion = null) } - private fun getFallbackAutocompletions( + private fun getDeterministicAutocompletions( prefix: String, limit: Int, ): List = @@ -1848,9 +1851,12 @@ abstract class GeneralKeyboardIME( */ private fun buildSuggestionBackground(isHighlighted: Boolean): Drawable { val density = resources.displayMetrics.density - val insetPx = (SUGGESTION_HIGHLIGHT_INSET_DP * density).toInt() + val horizontalInsetPx = (SUGGESTION_HIGHLIGHT_HORIZONTAL_INSET_DP * density).toInt() + val verticalInsetPx = (SUGGESTION_HIGHLIGHT_VERTICAL_INSET_DP * density).toInt() val underlineHeightPx = (SUGGESTION_UNDERLINE_HEIGHT_DP * density).toInt() - val underlineColorRes = if (isHighlighted) R.color.annotateGreen else R.color.annotateRed + val underlineExtraInsetPx = (SUGGESTION_UNDERLINE_EXTRA_HORIZONTAL_INSET_DP * density).toInt() + val underlineColorRes = + if (isHighlighted) R.color.autocomplete_highlight_indicator else R.color.autocomplete_alternate_indicator val underline = GradientDrawable().apply { shape = GradientDrawable.RECTANGLE @@ -1862,10 +1868,13 @@ abstract class GeneralKeyboardIME( val underlineIndex = layers.lastIndex val layered = LayerDrawable(layers).apply { + // The underline sits narrower than the highlight fill above it, matching the + // iOS reference design. + setLayerInset(underlineIndex, underlineExtraInsetPx, 0, underlineExtraInsetPx, 0) setLayerGravity(underlineIndex, Gravity.BOTTOM) setLayerHeight(underlineIndex, underlineHeightPx) } - return InsetDrawable(layered, insetPx, insetPx, insetPx, insetPx) + return InsetDrawable(layered, horizontalInsetPx, verticalInsetPx, horizontalInsetPx, verticalInsetPx) } /** diff --git a/app/src/main/java/be/scri/helpers/data/Trie.kt b/app/src/main/java/be/scri/helpers/data/Trie.kt index 611bb252..25f06226 100644 --- a/app/src/main/java/be/scri/helpers/data/Trie.kt +++ b/app/src/main/java/be/scri/helpers/data/Trie.kt @@ -82,8 +82,8 @@ class Trie { ) { if (results.size >= limit) return if (node.isWord) results.add(prefix) - for ((char, child) in node.children) { - collectWords(child, prefix + char, results, limit) + for (char in node.children.keys.sorted()) { + collectWords(node.children.getValue(char), prefix + char, results, limit) if (results.size >= limit) return } } diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 56a9fcab..c5254d8a 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -43,5 +43,7 @@ #AC6DEC #85C26F #FD9F5D + #85C26F + #FB5F6C @color/dark_keyboard_bg_color diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c299acc6..55fa86c0 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -800,6 +800,8 @@ #3D7946 #F85A39 + #3D7946 + #9F1722 @color/light_keyboard_bg_color From 0dc4c604dc7d3bb6976a9718bb5324bb2aa4ef77 Mon Sep 17 00:00:00 2001 From: Mounil Kanakhara Date: Sun, 12 Jul 2026 02:48:09 +0530 Subject: [PATCH 10/10] feat: color autocomplete underlines by grammatical gender per #407 Co-authored-by: Priyanshikaa1111 Signed-off-by: Mounil Kanakhara --- .../be/scri/services/GeneralKeyboardIME.kt | 129 ++++++++++++++---- app/src/main/res/values-night/colors.xml | 2 - app/src/main/res/values/colors.xml | 2 - 3 files changed, 104 insertions(+), 29 deletions(-) diff --git a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt index 7aa288a9..3dcf6ce9 100644 --- a/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt +++ b/app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt @@ -7,7 +7,12 @@ import android.R.color.white import android.content.Context import android.content.Intent import android.database.sqlite.SQLiteException +import android.graphics.Canvas import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PixelFormat import android.graphics.Typeface import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable @@ -1826,7 +1831,7 @@ abstract class GeneralKeyboardIME( button: Button, text: String, isHighlighted: Boolean = false, - showUnderline: Boolean = false, + underlineColors: List = emptyList(), ) { val isUserDarkMode = getIsDarkModeOrNot(applicationContext) val textColor = if (isUserDarkMode) Color.WHITE else "#1E1E1E".toColorInt() @@ -1835,7 +1840,12 @@ abstract class GeneralKeyboardIME( button.visibility = View.VISIBLE button.textSize = SUGGESTION_SIZE button.setOnClickListener(null) - button.background = if (showUnderline && text.isNotBlank()) buildSuggestionBackground(isHighlighted) else null + button.background = + if (text.isNotBlank() && (isHighlighted || underlineColors.isNotEmpty())) { + buildSuggestionBackground(isHighlighted, underlineColors) + } else { + null + } button.setTextColor(textColor) button.setTypeface(button.typeface, Typeface.NORMAL) button.setOnClickListener { @@ -1845,35 +1855,34 @@ abstract class GeneralKeyboardIME( } /** - * Builds the background for an autocomplete suggestion chip: a colored underline (green when - * the suggestion is highlighted as "obvious", red otherwise), plus a rounded, tinted fill - * behind the text when highlighted -- matching the Scribe-iOS reference design. + * Builds the background for a suggestion chip: a rounded, tinted fill behind the text when + * the suggestion is highlighted as "obvious" (matching the Scribe-iOS reference design), plus + * a colored underline showing the word's grammatical gender(s) when known (existing + * annotation feature -- see [AnnotationTextUtils]), narrower than the highlight fill above + * it. Nouns with multiple genders (e.g. German "Schild") get a split underline, one color + * per gender, per #407. */ - private fun buildSuggestionBackground(isHighlighted: Boolean): Drawable { + private fun buildSuggestionBackground( + isHighlighted: Boolean, + underlineColors: List, + ): Drawable { val density = resources.displayMetrics.density val horizontalInsetPx = (SUGGESTION_HIGHLIGHT_HORIZONTAL_INSET_DP * density).toInt() val verticalInsetPx = (SUGGESTION_HIGHLIGHT_VERTICAL_INSET_DP * density).toInt() val underlineHeightPx = (SUGGESTION_UNDERLINE_HEIGHT_DP * density).toInt() val underlineExtraInsetPx = (SUGGESTION_UNDERLINE_EXTRA_HORIZONTAL_INSET_DP * density).toInt() - val underlineColorRes = - if (isHighlighted) R.color.autocomplete_highlight_indicator else R.color.autocomplete_alternate_indicator - val underline = - GradientDrawable().apply { - shape = GradientDrawable.RECTANGLE - cornerRadius = underlineHeightPx / 2f - setColor(ContextCompat.getColor(applicationContext, underlineColorRes)) - } - val layers = if (isHighlighted) arrayOf(buildSuggestionHighlightFill(), underline) else arrayOf(underline) - val underlineIndex = layers.lastIndex - val layered = - LayerDrawable(layers).apply { - // The underline sits narrower than the highlight fill above it, matching the - // iOS reference design. - setLayerInset(underlineIndex, underlineExtraInsetPx, 0, underlineExtraInsetPx, 0) - setLayerGravity(underlineIndex, Gravity.BOTTOM) - setLayerHeight(underlineIndex, underlineHeightPx) - } + val layers = mutableListOf() + if (isHighlighted) layers.add(buildSuggestionHighlightFill()) + if (underlineColors.isNotEmpty()) layers.add(SplitColorDrawable(underlineColors)) + + val layered = LayerDrawable(layers.toTypedArray()) + if (underlineColors.isNotEmpty()) { + val underlineIndex = layers.lastIndex + layered.setLayerInset(underlineIndex, underlineExtraInsetPx, 0, underlineExtraInsetPx, 0) + layered.setLayerGravity(underlineIndex, Gravity.BOTTOM) + layered.setLayerHeight(underlineIndex, underlineHeightPx) + } return InsetDrawable(layered, horizontalInsetPx, verticalInsetPx, horizontalInsetPx, verticalInsetPx) } @@ -1945,7 +1954,7 @@ abstract class GeneralKeyboardIME( highlightedSuggestion: String? = null, ) { val isHighlighted = text.isNotBlank() && text.equals(highlightedSuggestion, ignoreCase = true) - setSuggestionButton(button, text, isHighlighted, showUnderline = true) + setSuggestionButton(button, text, isHighlighted, getGenderUnderlineColors(text)) if (text.isBlank()) { button.setOnClickListener(null) return @@ -1956,6 +1965,24 @@ abstract class GeneralKeyboardIME( } } + /** + * Looks up the given suggestion's grammatical gender(s) (if it's a known noun) and returns + * the matching annotation colors, reusing the existing gender-annotation feature (see + * [AnnotationTextUtils]) rather than inventing a new color scheme. Returns an empty list if + * the word has no known gender -- most suggestions won't, and that's expected; it just means + * no underline is drawn for that chip. Words with multiple genders (e.g. German "Schild", + * which is both masculine and neuter) get one color per gender, per #407. + */ + private fun getGenderUnderlineColors(text: String): List { + if (text.isBlank()) return emptyList() + val genders = findGenderForLastWord(nounKeywords, text) ?: return emptyList() + return genders + .map { handleColorAndTextForNounType(it, language, applicationContext).first } + .filter { it != R.color.transparent } + .distinct() + .map { ContextCompat.getColor(applicationContext, it) } + } + /** * Deletes the word currently being typed (immediately before the cursor) and commits * [text] in its place. @@ -2203,3 +2230,55 @@ abstract class GeneralKeyboardIME( binding.clipboardItemsList.visibility = if (items.isEmpty()) android.view.View.GONE else android.view.View.VISIBLE } } + +/** + * A pill-shaped drawable that splits its bounds into equal horizontal segments, one per color. + * Used to render a noun's gender annotation as a single-color underline, or a half-and-half + * split underline for nouns with more than one gender (e.g. German "Schild"), per #407. + */ +private class SplitColorDrawable( + private val colors: List, +) : Drawable() { + private val paint = Paint(Paint.ANTI_ALIAS_FLAG) + + override fun draw(canvas: Canvas) { + val b = bounds + if (colors.isEmpty() || b.width() <= 0 || b.height() <= 0) return + + val cornerRadius = b.height() / 2f + val clipPath = + Path().apply { + addRoundRect( + b.left.toFloat(), + b.top.toFloat(), + b.right.toFloat(), + b.bottom.toFloat(), + cornerRadius, + cornerRadius, + Path.Direction.CW, + ) + } + canvas.save() + canvas.clipPath(clipPath) + + val segmentWidth = b.width().toFloat() / colors.size + colors.forEachIndexed { index, color -> + paint.color = color + val left = b.left + segmentWidth * index + val right = if (index == colors.lastIndex) b.right.toFloat() else b.left + segmentWidth * (index + 1) + canvas.drawRect(left, b.top.toFloat(), right, b.bottom.toFloat(), paint) + } + canvas.restore() + } + + override fun setAlpha(alpha: Int) { + paint.alpha = alpha + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + paint.colorFilter = colorFilter + } + + @Deprecated("Deprecated in Java", ReplaceWith("PixelFormat.TRANSLUCENT")) + override fun getOpacity(): Int = PixelFormat.TRANSLUCENT +} diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index c5254d8a..56a9fcab 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -43,7 +43,5 @@ #AC6DEC #85C26F #FD9F5D - #85C26F - #FB5F6C @color/dark_keyboard_bg_color diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 55fa86c0..c299acc6 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -800,8 +800,6 @@ #3D7946 #F85A39 - #3D7946 - #9F1722 @color/light_keyboard_bg_color