From 441ed9c8d5983e6d2cacf6269ccb3187d99e62aa Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Fri, 26 Dec 2025 14:26:46 +0530 Subject: [PATCH 1/5] feat: add tools popup to comma (or whatever) key This change introduces a popup menu on the second left key in the bottom row (often the comma), providing quick access to emoji and settings. - A new `keyRole` attribute to identify special keys like the new "tools" key. The comma key on most letter layouts is now designated as the `tools` key. - A long press on the comma key reveals a popup with icons for emoji and settings. - When the dedicated emoji key is enabled in settings, the emoji icon is removed from the tools popup and its secondary icon hint. - Introduced new key codes (`KEYCODE_POPUP_EMOJI`, `KEYCODE_POPUP_SETTINGS`) to handle actions from the tools popup. - Refactored the secondary icon drawing logi for better reusability. Refs: https://github.com/FossifyOrg/Keyboard/issues/62 --- .../fossify/keyboard/helpers/MyKeyboard.kt | 11 +++- .../keyboard/services/SimpleKeyboardIME.kt | 21 ++++++- .../fossify/keyboard/views/MyKeyboardView.kt | 59 +++++++++++-------- app/src/main/res/values/attrs.xml | 2 + .../res/xml/keys_letters_belarusian_cyrl.xml | 13 ++-- .../res/xml/keys_letters_belarusian_latn.xml | 5 +- app/src/main/res/xml/keys_letters_bengali.xml | 13 ++-- .../main/res/xml/keys_letters_bulgarian.xml | 13 ++-- app/src/main/res/xml/keys_letters_chuvash.xml | 5 +- .../res/xml/keys_letters_czech_qwerty.xml | 17 +++--- .../res/xml/keys_letters_czech_qwertz.xml | 17 +++--- app/src/main/res/xml/keys_letters_danish.xml | 5 +- app/src/main/res/xml/keys_letters_dutch.xml | 37 +++++------- .../res/xml/keys_letters_english_asset.xml | 5 +- .../res/xml/keys_letters_english_colemak.xml | 5 +- .../xml/keys_letters_english_colemakdh.xml | 5 +- .../res/xml/keys_letters_english_dvorak.xml | 5 +- .../res/xml/keys_letters_english_niro.xml | 5 +- .../res/xml/keys_letters_english_qwerty.xml | 5 +- .../res/xml/keys_letters_english_qwertz.xml | 13 ++-- .../res/xml/keys_letters_english_soul.xml | 5 +- .../res/xml/keys_letters_english_workman.xml | 5 +- .../main/res/xml/keys_letters_esperanto.xml | 5 +- .../res/xml/keys_letters_french_azerty.xml | 13 ++-- .../main/res/xml/keys_letters_french_bepo.xml | 17 +++--- app/src/main/res/xml/keys_letters_german.xml | 5 +- .../res/xml/keys_letters_german_qwertz.xml | 13 ++-- app/src/main/res/xml/keys_letters_greek.xml | 17 +++--- app/src/main/res/xml/keys_letters_hebrew.xml | 5 +- app/src/main/res/xml/keys_letters_italian.xml | 8 ++- .../res/xml/keys_letters_kabyle_azerty.xml | 48 ++++++++------- app/src/main/res/xml/keys_letters_latvian.xml | 10 ++-- .../main/res/xml/keys_letters_lithuanian.xml | 7 ++- .../main/res/xml/keys_letters_norwegian.xml | 5 +- app/src/main/res/xml/keys_letters_polish.xml | 5 +- .../main/res/xml/keys_letters_portuguese.xml | 7 ++- .../xml/keys_letters_portuguese_hcesar.xml | 5 +- .../main/res/xml/keys_letters_romanian.xml | 5 +- app/src/main/res/xml/keys_letters_russian.xml | 13 ++-- .../main/res/xml/keys_letters_slovenian.xml | 13 ++-- .../res/xml/keys_letters_spanish_qwerty.xml | 13 ++-- app/src/main/res/xml/keys_letters_swedish.xml | 5 +- app/src/main/res/xml/keys_letters_turkish.xml | 20 +++---- .../main/res/xml/keys_letters_turkish_q.xml | 5 +- .../main/res/xml/keys_letters_ukrainian.xml | 5 +- app/src/main/res/xml/keys_symbols.xml | 4 +- app/src/main/res/xml/keys_symbols_alt.xml | 4 +- app/src/main/res/xml/keys_symbols_shift.xml | 4 +- app/src/main/res/xml/popup_tools.xml | 8 +++ .../res/xml/popup_tools_with_emoji_key.xml | 11 ++++ 50 files changed, 361 insertions(+), 190 deletions(-) create mode 100644 app/src/main/res/xml/popup_tools.xml create mode 100644 app/src/main/res/xml/popup_tools_with_emoji_key.xml diff --git a/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt b/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt index 592e7d30..510ede8f 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt @@ -67,6 +67,11 @@ class MyKeyboard { const val KEYCODE_DELETE = -5 const val KEYCODE_SPACE = 32 const val KEYCODE_EMOJI_OR_LANGUAGE = -6 + const val KEYCODE_POPUP_EMOJI = -7 + const val KEYCODE_POPUP_SETTINGS = -8 + + // Key roles for special keys + const val KEY_ROLE_TOOLS = "tools" fun getDimensionOrFraction(a: TypedArray, index: Int, base: Int, defValue: Int): Int { val value = a.peekValue(index) ?: return defValue @@ -169,6 +174,9 @@ class MyKeyboard { /** Popup characters showing after long pressing the key */ var popupCharacters: CharSequence? = null + /** Role identifier for special keys (e.g., "tools" for the tools popup host key) */ + var role: String? = null + /** * Flags that specify the anchoring to edges of the keyboard for detecting touch events that are just out of the boundary of the key. * This is a bit mask of [MyKeyboard.EDGE_LEFT], [MyKeyboard.EDGE_RIGHT]. @@ -221,7 +229,7 @@ class MyKeyboard { secondaryIcon?.setBounds(0, 0, secondaryIcon!!.intrinsicWidth, secondaryIcon!!.intrinsicHeight) topSmallNumber = a.getString(R.styleable.MyKeyboard_Key_topSmallNumber) ?: "" - + role = a.getString(R.styleable.MyKeyboard_Key_keyRole) a.recycle() } @@ -271,7 +279,6 @@ class MyKeyboard { * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. * @param enterKeyType determines what icon should we show on Enter key */ - @JvmOverloads constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int) { mDisplayWidth = context.resources.displayMetrics.widthPixels mDefaultHorizontalGap = 0 diff --git a/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt b/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt index 02b2cee9..7ed3c63a 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt @@ -1,6 +1,7 @@ package org.fossify.keyboard.services import android.annotation.SuppressLint +import android.content.Intent import android.content.SharedPreferences import android.graphics.Bitmap import android.graphics.drawable.Icon @@ -37,6 +38,7 @@ import androidx.core.view.updatePadding import org.fossify.commons.extensions.* import org.fossify.commons.helpers.* import org.fossify.keyboard.R +import org.fossify.keyboard.activities.SettingsActivity import org.fossify.keyboard.databinding.KeyboardViewKeyboardBinding import org.fossify.keyboard.extensions.config import org.fossify.keyboard.extensions.getKeyboardBackgroundColor @@ -268,6 +270,13 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared } } + MyKeyboard.KEYCODE_POPUP_EMOJI -> keyboardView?.openEmojiPalette() + MyKeyboard.KEYCODE_POPUP_SETTINGS -> Intent(this, SettingsActivity::class.java) + .apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(this) + } + else -> { var codeChar = code.toChar() val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text @@ -610,7 +619,7 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared } } - if (keyboardMode != KEYBOARD_LETTERS) return keyboard + if (keyboardMode != KEYBOARD_LETTERS) return@let val emojiKeyIndex = keys.indexOfFirst { it.code == MyKeyboard.KEYCODE_EMOJI_OR_LANGUAGE } if (emojiKeyIndex != -1 && spaceKeyIndex != -1) { val emojiKey = keys[emojiKeyIndex] @@ -634,6 +643,16 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared } } } + + // When emoji key is enabled, show settings-only popup with no hint on tools key + if (config.showEmojiKey) { + val currentKeys = keyboard.mKeys ?: return keyboard + val toolsKey = currentKeys.firstOrNull { it.role == MyKeyboard.KEY_ROLE_TOOLS } + if (toolsKey != null) { + toolsKey.popupResId = R.xml.popup_tools + toolsKey.secondaryIcon = null + } + } } return keyboard } diff --git a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt index 1918e787..59ee1350 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt @@ -722,9 +722,9 @@ class MyKeyboardView @JvmOverloads constructor( canvas.drawText(row, key.width / 2f, startY + textSize * index, paint) } - if (key.topSmallNumber.isNotEmpty() && !(context.config.showNumbersRow && Regex("\\d").matches( - key.topSmallNumber - )) + if ( + key.topSmallNumber.isNotEmpty() + && !(context.config.showNumbersRow && Regex("\\d").matches(key.topSmallNumber)) ) { val bounds = Rect().also { smallLetterPaint.getTextBounds( @@ -749,6 +749,9 @@ class MyKeyboardView @JvmOverloads constructor( ) } + // Draw secondary icons for label-based keys + drawSecondaryIcon(key, canvas, textColor) + // Turn off drop shadow paint.setShadowLayer(0f, 0f, 0f, 0) } else if (key.icon != null && mKeyboard != null) { @@ -779,10 +782,9 @@ class MyKeyboardView @JvmOverloads constructor( val keyIcon = key.icon!! val secondaryIcon = key.secondaryIcon if (secondaryIcon != null) { + // When secondary icon exists, shrink main icon to 90% val keyIconWidth = (keyIcon.intrinsicWidth * 0.9f).toInt() val keyIconHeight = (keyIcon.intrinsicHeight * 0.9f).toInt() - val secondaryIconWidth = (secondaryIcon.intrinsicWidth * 0.5f).toInt() - val secondaryIconHeight = (secondaryIcon.intrinsicHeight * 0.5f).toInt() val centerX = key.width / 2 val centerY = key.height / 2 @@ -798,20 +800,7 @@ class MyKeyboardView @JvmOverloads constructor( ) keyIcon.draw(canvas) - val secondaryIconPaddingRight = 10 - val secondaryIconLeft = - key.width - secondaryIconPaddingRight - secondaryIconWidth - val secondaryIconRight = secondaryIconLeft + secondaryIconWidth - - val secondaryIconTop = 14 // This will act as a topPadding - val secondaryIconBottom = secondaryIconTop + secondaryIconHeight - - secondaryIcon.setBounds( - secondaryIconLeft, secondaryIconTop, secondaryIconRight, secondaryIconBottom - ) - secondaryIcon.draw(canvas) - - secondaryIcon.draw(canvas) + drawSecondaryIcon(key, canvas, textColor) } else { val drawableX = (key.width - keyIcon.intrinsicWidth) / 2 val drawableY = (key.height - keyIcon.intrinsicHeight) / 2 @@ -897,6 +886,25 @@ class MyKeyboardView @JvmOverloads constructor( return resources.getDrawable(drawableId, context.theme) } + private fun drawSecondaryIcon(key: MyKeyboard.Key, canvas: Canvas, textColor: Int) { + val secondaryIcon = key.secondaryIcon ?: return + secondaryIcon.applyColorFilter( + if (key.pressed) textColor else mTextColor.adjustAlpha(0.6f) + ) + val secondaryIconWidth = (secondaryIcon.intrinsicWidth * 0.5f).toInt() + val secondaryIconHeight = (secondaryIcon.intrinsicHeight * 0.5f).toInt() + val secondaryIconPaddingRight = 10 + val secondaryIconLeft = key.width - secondaryIconPaddingRight - secondaryIconWidth + val secondaryIconRight = secondaryIconLeft + secondaryIconWidth + val secondaryIconTop = 14 + val secondaryIconBottom = secondaryIconTop + secondaryIconHeight + + secondaryIcon.setBounds( + secondaryIconLeft, secondaryIconTop, secondaryIconRight, secondaryIconBottom + ) + secondaryIcon.draw(canvas) + } + private fun handleClipboard() { if (mToolbarHolder != null && mPopupParent.id != R.id.mini_keyboard_view && context.config.showClipboardContent) { val clipboardContent = context.getCurrentClip() @@ -1205,9 +1213,11 @@ class MyKeyboardView @JvmOverloads constructor( // For 'number' and 'phone' keyboards the count of popup keys might be bigger than count of keys in the main keyboard. // And therefore the width of the key might be smaller than width declared in MyKeyboard.Key.width for the main keyboard. - val popupKeyWidth = popupKey.calcKeyWidth( - containerWidth = mMiniKeyboardContainer?.measuredWidth ?: width - ) + val popupKeyWidth = if (popupKey.popupCharacters != null) { + popupKey.calcKeyWidth(containerWidth = mMiniKeyboardContainer?.measuredWidth ?: width) + } else { + popupKey.width + } if (mMiniKeyboardContainer == null) { val inflater = @@ -1243,8 +1253,9 @@ class MyKeyboardView @JvmOverloads constructor( mPopupX = popupKey.x mPopupY = popupKey.y - val widthToUse = - mMiniKeyboardContainer!!.measuredWidth - (popupKey.popupCharacters!!.length / 2) * popupKeyWidth + // Use popupCharacters length if available, otherwise use actual key count from the loaded keyboard + val popupKeyCount = popupKey.popupCharacters?.length ?: mMiniKeyboard!!.mKeys.size + val widthToUse = mMiniKeyboardContainer!!.measuredWidth - (popupKeyCount / 2) * popupKeyWidth mPopupX = mPopupX + popupKeyWidth - widthToUse mPopupY -= mMiniKeyboardContainer!!.measuredHeight val x = mPopupX + mCoordinates[0] diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 994aa3e2..a228eded 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -38,5 +38,7 @@ + + diff --git a/app/src/main/res/xml/keys_letters_belarusian_cyrl.xml b/app/src/main/res/xml/keys_letters_belarusian_cyrl.xml index 0251a659..4e72eecb 100644 --- a/app/src/main/res/xml/keys_letters_belarusian_cyrl.xml +++ b/app/src/main/res/xml/keys_letters_belarusian_cyrl.xml @@ -132,17 +132,17 @@ app:keyWidth="8%p" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> @@ -164,7 +164,10 @@ app:keyWidth="15%p" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> - + + app:keyLabel="l" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> - + + app:keyLabel="l" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyLabel="a" /> - - - - - - - + + + + + + + - + - + - + + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyRole="tools" + app:keyWidth="10%p" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + - @@ -154,22 +154,25 @@ app:keyWidth="15%p" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyRole="tools" + app:keyWidth="10%p" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> - - + + - - + + @@ -125,9 +129,10 @@ app:popupCharacters="čçćč" app:popupKeyboard="@xml/keyboard_popup_template" /> - + + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" /> + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyRole="tools" + app:keyWidth="10%p" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:secondaryKeyIcon="@drawable/ic_language_outlined" /> + app:popupKeyboard="@xml/keyboard_popup_template" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + + - - + app:popupKeyboard="@xml/keyboard_popup_template" /> @@ -119,8 +118,7 @@ app:keyEdgeFlags="left" app:keyIcon="@drawable/ic_caps_outline_vector" app:keyWidth="15%p" /> - + - + + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyWidth="10%p" + app:keyRole="tools" + app:popupKeyboard="@xml/popup_tools_with_emoji_key" + app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" /> + app:keyRole="tools" + app:keyWidth="10%p" + app:popupKeyboard="@xml/popup_tools" /> + app:keyRole="tools" + app:keyWidth="10%p" + app:popupKeyboard="@xml/popup_tools" /> + app:keyRole="tools" + app:keyWidth="10%p" + app:popupKeyboard="@xml/popup_tools" /> + + + + + diff --git a/app/src/main/res/xml/popup_tools_with_emoji_key.xml b/app/src/main/res/xml/popup_tools_with_emoji_key.xml new file mode 100644 index 00000000..5336f289 --- /dev/null +++ b/app/src/main/res/xml/popup_tools_with_emoji_key.xml @@ -0,0 +1,11 @@ + + + + + + + From 95f040ce98ba507bf5b86d92a8bfdd87146f80c6 Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Fri, 26 Dec 2025 14:51:40 +0530 Subject: [PATCH 2/5] refactor: extract space bar and emoji/language long press handlers --- .../keyboard/services/SimpleKeyboardIME.kt | 2 +- .../fossify/keyboard/views/MyKeyboardView.kt | 47 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt b/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt index 7ed3c63a..02784ac6 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt @@ -619,7 +619,7 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared } } - if (keyboardMode != KEYBOARD_LETTERS) return@let + if (keyboardMode != KEYBOARD_LETTERS) return keyboard val emojiKeyIndex = keys.indexOfFirst { it.code == MyKeyboard.KEYCODE_EMOJI_OR_LANGUAGE } if (emojiKeyIndex != -1 && spaceKeyIndex != -1) { val emojiKey = keys[emojiKeyIndex] diff --git a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt index 59ee1350..e03f7f8f 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt @@ -1186,26 +1186,9 @@ class MyKeyboardView @JvmOverloads constructor( */ private fun onLongPress(popupKey: MyKeyboard.Key, me: MotionEvent): Boolean { if (popupKey.code == KEYCODE_SPACE) { - return if (!mCursorControlActive) { - setCurrentKeyPressed(false) - mRepeatKeyIndex = NOT_A_KEY - mHandler?.removeMessages(MSG_REPEAT) - vibrateIfNeeded() - SwitchLanguageDialog(this) { - mOnKeyboardActionListener?.reloadKeyboard() - } - true - } else false + return onSpaceBarLongPressed() } else if (popupKey.code == KEYCODE_EMOJI_OR_LANGUAGE) { - setCurrentKeyPressed(false) - if (context.config.showEmojiKey) { - openEmojiPalette() - } else { - SwitchLanguageDialog(this) { - mOnKeyboardActionListener?.reloadKeyboard() - } - } - return true + return onEmojiOrLanguageLongPressed() } else { val popupKeyboardId = popupKey.popupResId if (popupKeyboardId != 0) { @@ -1617,6 +1600,32 @@ class MyKeyboardView @JvmOverloads constructor( setupStoredClips() } + private fun onSpaceBarLongPressed(): Boolean { + return if (!mCursorControlActive) { + setCurrentKeyPressed(false) + mRepeatKeyIndex = NOT_A_KEY + mHandler?.removeMessages(MSG_REPEAT) + vibrateIfNeeded() + SwitchLanguageDialog(this) { + mOnKeyboardActionListener?.reloadKeyboard() + } + true + } else false + } + + private fun onEmojiOrLanguageLongPressed(): Boolean { + setCurrentKeyPressed(false) + if (context.config.showEmojiKey) { + openEmojiPalette() + } else { + SwitchLanguageDialog(this) { + mOnKeyboardActionListener?.reloadKeyboard() + } + } + + return true + } + private fun setupStoredClips() { ensureBackgroundThread { val clips = ArrayList() From 10355d4c7a7d2b066ecd71850e63380f35257518 Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Fri, 26 Dec 2025 14:59:00 +0530 Subject: [PATCH 3/5] fix: use proper colors for popup icons --- .../kotlin/org/fossify/keyboard/views/MyKeyboardView.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt index e03f7f8f..d7abca9b 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt @@ -99,6 +99,8 @@ import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_DELETE import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_EMOJI_OR_LANGUAGE import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_ENTER import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_MODE_CHANGE +import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_POPUP_EMOJI +import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_POPUP_SETTINGS import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_SHIFT import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_SPACE import org.fossify.keyboard.helpers.MyKeyboard.Companion.KEYCODE_SYMBOLS_MODE_CHANGE @@ -768,7 +770,10 @@ class MyKeyboardView @JvmOverloads constructor( val contrastColor = mPrimaryColor.getContrastColor() key.icon!!.applyColorFilter(contrastColor) key.secondaryIcon?.applyColorFilter(contrastColor.adjustAlpha(0.6f)) - } else if (code == KEYCODE_DELETE || code == KEYCODE_SHIFT || code == KEYCODE_EMOJI_OR_LANGUAGE) { + } else if ( + code == KEYCODE_DELETE || code == KEYCODE_SHIFT || code == KEYCODE_EMOJI_OR_LANGUAGE + || code == KEYCODE_POPUP_EMOJI || code == KEYCODE_POPUP_SETTINGS + ) { key.icon!!.applyColorFilter(textColor) key.secondaryIcon?.applyColorFilter( if (key.pressed) { From c5f4cae4e80ec7664eee578717a55c01d193c381 Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Fri, 26 Dec 2025 15:10:41 +0530 Subject: [PATCH 4/5] fix: avoid clearing popup when numbers row is enabled --- app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt b/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt index 510ede8f..188d6d4a 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt @@ -385,8 +385,9 @@ class MyKeyboard { if (context.config.showNumbersRow) { // Removes numbers (i.e 0-9) from the popupCharacters if numbers row is enabled key.apply { + val hadPopupCharacters = popupCharacters != null popupCharacters = popupCharacters?.replace(Regex("\\d+"), "") - if (popupCharacters.isNullOrEmpty()) { + if (hadPopupCharacters && popupCharacters.isNullOrEmpty()) { popupResId = 0 } } From 99d397a593f2a2c8f91fb652552c9b887b96a431 Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Fri, 26 Dec 2025 15:17:40 +0530 Subject: [PATCH 5/5] style: simplify condition --- .../kotlin/org/fossify/keyboard/views/MyKeyboardView.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt index d7abca9b..01ac4a48 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt @@ -771,8 +771,13 @@ class MyKeyboardView @JvmOverloads constructor( key.icon!!.applyColorFilter(contrastColor) key.secondaryIcon?.applyColorFilter(contrastColor.adjustAlpha(0.6f)) } else if ( - code == KEYCODE_DELETE || code == KEYCODE_SHIFT || code == KEYCODE_EMOJI_OR_LANGUAGE - || code == KEYCODE_POPUP_EMOJI || code == KEYCODE_POPUP_SETTINGS + code in arrayOf( + KEYCODE_DELETE, + KEYCODE_SHIFT, + KEYCODE_EMOJI_OR_LANGUAGE, + KEYCODE_POPUP_EMOJI, + KEYCODE_POPUP_SETTINGS + ) ) { key.icon!!.applyColorFilter(textColor) key.secondaryIcon?.applyColorFilter(