Skip to content

Commit 441ed9c

Browse files
committed
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: #62
1 parent b272c82 commit 441ed9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+361
-190
lines changed

app/src/main/kotlin/org/fossify/keyboard/helpers/MyKeyboard.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ class MyKeyboard {
6767
const val KEYCODE_DELETE = -5
6868
const val KEYCODE_SPACE = 32
6969
const val KEYCODE_EMOJI_OR_LANGUAGE = -6
70+
const val KEYCODE_POPUP_EMOJI = -7
71+
const val KEYCODE_POPUP_SETTINGS = -8
72+
73+
// Key roles for special keys
74+
const val KEY_ROLE_TOOLS = "tools"
7075

7176
fun getDimensionOrFraction(a: TypedArray, index: Int, base: Int, defValue: Int): Int {
7277
val value = a.peekValue(index) ?: return defValue
@@ -169,6 +174,9 @@ class MyKeyboard {
169174
/** Popup characters showing after long pressing the key */
170175
var popupCharacters: CharSequence? = null
171176

177+
/** Role identifier for special keys (e.g., "tools" for the tools popup host key) */
178+
var role: String? = null
179+
172180
/**
173181
* Flags that specify the anchoring to edges of the keyboard for detecting touch events that are just out of the boundary of the key.
174182
* This is a bit mask of [MyKeyboard.EDGE_LEFT], [MyKeyboard.EDGE_RIGHT].
@@ -221,7 +229,7 @@ class MyKeyboard {
221229
secondaryIcon?.setBounds(0, 0, secondaryIcon!!.intrinsicWidth, secondaryIcon!!.intrinsicHeight)
222230

223231
topSmallNumber = a.getString(R.styleable.MyKeyboard_Key_topSmallNumber) ?: ""
224-
232+
role = a.getString(R.styleable.MyKeyboard_Key_keyRole)
225233

226234
a.recycle()
227235
}
@@ -271,7 +279,6 @@ class MyKeyboard {
271279
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
272280
* @param enterKeyType determines what icon should we show on Enter key
273281
*/
274-
@JvmOverloads
275282
constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int) {
276283
mDisplayWidth = context.resources.displayMetrics.widthPixels
277284
mDefaultHorizontalGap = 0

app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.fossify.keyboard.services
22

33
import android.annotation.SuppressLint
4+
import android.content.Intent
45
import android.content.SharedPreferences
56
import android.graphics.Bitmap
67
import android.graphics.drawable.Icon
@@ -37,6 +38,7 @@ import androidx.core.view.updatePadding
3738
import org.fossify.commons.extensions.*
3839
import org.fossify.commons.helpers.*
3940
import org.fossify.keyboard.R
41+
import org.fossify.keyboard.activities.SettingsActivity
4042
import org.fossify.keyboard.databinding.KeyboardViewKeyboardBinding
4143
import org.fossify.keyboard.extensions.config
4244
import org.fossify.keyboard.extensions.getKeyboardBackgroundColor
@@ -268,6 +270,13 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared
268270
}
269271
}
270272

273+
MyKeyboard.KEYCODE_POPUP_EMOJI -> keyboardView?.openEmojiPalette()
274+
MyKeyboard.KEYCODE_POPUP_SETTINGS -> Intent(this, SettingsActivity::class.java)
275+
.apply {
276+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
277+
startActivity(this)
278+
}
279+
271280
else -> {
272281
var codeChar = code.toChar()
273282
val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text
@@ -610,7 +619,7 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared
610619
}
611620
}
612621

613-
if (keyboardMode != KEYBOARD_LETTERS) return keyboard
622+
if (keyboardMode != KEYBOARD_LETTERS) return@let
614623
val emojiKeyIndex = keys.indexOfFirst { it.code == MyKeyboard.KEYCODE_EMOJI_OR_LANGUAGE }
615624
if (emojiKeyIndex != -1 && spaceKeyIndex != -1) {
616625
val emojiKey = keys[emojiKeyIndex]
@@ -634,6 +643,16 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared
634643
}
635644
}
636645
}
646+
647+
// When emoji key is enabled, show settings-only popup with no hint on tools key
648+
if (config.showEmojiKey) {
649+
val currentKeys = keyboard.mKeys ?: return keyboard
650+
val toolsKey = currentKeys.firstOrNull { it.role == MyKeyboard.KEY_ROLE_TOOLS }
651+
if (toolsKey != null) {
652+
toolsKey.popupResId = R.xml.popup_tools
653+
toolsKey.secondaryIcon = null
654+
}
655+
}
637656
}
638657
return keyboard
639658
}

app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -722,9 +722,9 @@ class MyKeyboardView @JvmOverloads constructor(
722722
canvas.drawText(row, key.width / 2f, startY + textSize * index, paint)
723723
}
724724

725-
if (key.topSmallNumber.isNotEmpty() && !(context.config.showNumbersRow && Regex("\\d").matches(
726-
key.topSmallNumber
727-
))
725+
if (
726+
key.topSmallNumber.isNotEmpty()
727+
&& !(context.config.showNumbersRow && Regex("\\d").matches(key.topSmallNumber))
728728
) {
729729
val bounds = Rect().also {
730730
smallLetterPaint.getTextBounds(
@@ -749,6 +749,9 @@ class MyKeyboardView @JvmOverloads constructor(
749749
)
750750
}
751751

752+
// Draw secondary icons for label-based keys
753+
drawSecondaryIcon(key, canvas, textColor)
754+
752755
// Turn off drop shadow
753756
paint.setShadowLayer(0f, 0f, 0f, 0)
754757
} else if (key.icon != null && mKeyboard != null) {
@@ -779,10 +782,9 @@ class MyKeyboardView @JvmOverloads constructor(
779782
val keyIcon = key.icon!!
780783
val secondaryIcon = key.secondaryIcon
781784
if (secondaryIcon != null) {
785+
// When secondary icon exists, shrink main icon to 90%
782786
val keyIconWidth = (keyIcon.intrinsicWidth * 0.9f).toInt()
783787
val keyIconHeight = (keyIcon.intrinsicHeight * 0.9f).toInt()
784-
val secondaryIconWidth = (secondaryIcon.intrinsicWidth * 0.5f).toInt()
785-
val secondaryIconHeight = (secondaryIcon.intrinsicHeight * 0.5f).toInt()
786788

787789
val centerX = key.width / 2
788790
val centerY = key.height / 2
@@ -798,20 +800,7 @@ class MyKeyboardView @JvmOverloads constructor(
798800
)
799801
keyIcon.draw(canvas)
800802

801-
val secondaryIconPaddingRight = 10
802-
val secondaryIconLeft =
803-
key.width - secondaryIconPaddingRight - secondaryIconWidth
804-
val secondaryIconRight = secondaryIconLeft + secondaryIconWidth
805-
806-
val secondaryIconTop = 14 // This will act as a topPadding
807-
val secondaryIconBottom = secondaryIconTop + secondaryIconHeight
808-
809-
secondaryIcon.setBounds(
810-
secondaryIconLeft, secondaryIconTop, secondaryIconRight, secondaryIconBottom
811-
)
812-
secondaryIcon.draw(canvas)
813-
814-
secondaryIcon.draw(canvas)
803+
drawSecondaryIcon(key, canvas, textColor)
815804
} else {
816805
val drawableX = (key.width - keyIcon.intrinsicWidth) / 2
817806
val drawableY = (key.height - keyIcon.intrinsicHeight) / 2
@@ -897,6 +886,25 @@ class MyKeyboardView @JvmOverloads constructor(
897886
return resources.getDrawable(drawableId, context.theme)
898887
}
899888

889+
private fun drawSecondaryIcon(key: MyKeyboard.Key, canvas: Canvas, textColor: Int) {
890+
val secondaryIcon = key.secondaryIcon ?: return
891+
secondaryIcon.applyColorFilter(
892+
if (key.pressed) textColor else mTextColor.adjustAlpha(0.6f)
893+
)
894+
val secondaryIconWidth = (secondaryIcon.intrinsicWidth * 0.5f).toInt()
895+
val secondaryIconHeight = (secondaryIcon.intrinsicHeight * 0.5f).toInt()
896+
val secondaryIconPaddingRight = 10
897+
val secondaryIconLeft = key.width - secondaryIconPaddingRight - secondaryIconWidth
898+
val secondaryIconRight = secondaryIconLeft + secondaryIconWidth
899+
val secondaryIconTop = 14
900+
val secondaryIconBottom = secondaryIconTop + secondaryIconHeight
901+
902+
secondaryIcon.setBounds(
903+
secondaryIconLeft, secondaryIconTop, secondaryIconRight, secondaryIconBottom
904+
)
905+
secondaryIcon.draw(canvas)
906+
}
907+
900908
private fun handleClipboard() {
901909
if (mToolbarHolder != null && mPopupParent.id != R.id.mini_keyboard_view && context.config.showClipboardContent) {
902910
val clipboardContent = context.getCurrentClip()
@@ -1205,9 +1213,11 @@ class MyKeyboardView @JvmOverloads constructor(
12051213

12061214
// For 'number' and 'phone' keyboards the count of popup keys might be bigger than count of keys in the main keyboard.
12071215
// And therefore the width of the key might be smaller than width declared in MyKeyboard.Key.width for the main keyboard.
1208-
val popupKeyWidth = popupKey.calcKeyWidth(
1209-
containerWidth = mMiniKeyboardContainer?.measuredWidth ?: width
1210-
)
1216+
val popupKeyWidth = if (popupKey.popupCharacters != null) {
1217+
popupKey.calcKeyWidth(containerWidth = mMiniKeyboardContainer?.measuredWidth ?: width)
1218+
} else {
1219+
popupKey.width
1220+
}
12111221

12121222
if (mMiniKeyboardContainer == null) {
12131223
val inflater =
@@ -1243,8 +1253,9 @@ class MyKeyboardView @JvmOverloads constructor(
12431253
mPopupX = popupKey.x
12441254
mPopupY = popupKey.y
12451255

1246-
val widthToUse =
1247-
mMiniKeyboardContainer!!.measuredWidth - (popupKey.popupCharacters!!.length / 2) * popupKeyWidth
1256+
// Use popupCharacters length if available, otherwise use actual key count from the loaded keyboard
1257+
val popupKeyCount = popupKey.popupCharacters?.length ?: mMiniKeyboard!!.mKeys.size
1258+
val widthToUse = mMiniKeyboardContainer!!.measuredWidth - (popupKeyCount / 2) * popupKeyWidth
12481259
mPopupX = mPopupX + popupKeyWidth - widthToUse
12491260
mPopupY -= mMiniKeyboardContainer!!.measuredHeight
12501261
val x = mPopupX + mCoordinates[0]

app/src/main/res/values/attrs.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,7 @@
3838
<attr name="secondaryKeyIcon" format="reference" />
3939
<!-- Top small number shown above letters of the first row. -->
4040
<attr name="topSmallNumber" format="string" />
41+
<!-- Role identifier for special keys (e.g., "tools" for the tools popup host key). -->
42+
<attr name="keyRole" format="string" />
4143
</declare-styleable>
4244
</resources>

app/src/main/res/xml/keys_letters_belarusian_cyrl.xml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,17 @@
132132
app:keyWidth="8%p" />
133133
<Key
134134
app:keyLabel="і"
135+
app:keyWidth="8%p"
135136
app:popupCharacters="иї"
136-
app:popupKeyboard="@xml/keyboard_popup_template"
137-
app:keyWidth="8%p" />
137+
app:popupKeyboard="@xml/keyboard_popup_template" />
138138
<Key
139139
app:keyLabel="т"
140140
app:keyWidth="8%p" />
141141
<Key
142142
app:keyLabel="ь"
143+
app:keyWidth="8%p"
143144
app:popupCharacters="ъ"
144-
app:popupKeyboard="@xml/keyboard_popup_template"
145-
app:keyWidth="8%p" />
145+
app:popupKeyboard="@xml/keyboard_popup_template" />
146146
<Key
147147
app:keyLabel="б"
148148
app:keyWidth="8%p" />
@@ -164,7 +164,10 @@
164164
app:keyWidth="15%p" />
165165
<Key
166166
app:keyLabel=","
167-
app:keyWidth="10%p" />
167+
app:keyWidth="10%p"
168+
app:keyRole="tools"
169+
app:popupKeyboard="@xml/popup_tools_with_emoji_key"
170+
app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" />
168171
<Key
169172
app:code="-6"
170173
app:keyEdgeFlags="left"

app/src/main/res/xml/keys_letters_belarusian_latn.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,10 @@
148148
app:keyWidth="15%p" />
149149
<Key
150150
app:keyLabel=","
151-
app:keyWidth="10%p" />
151+
app:keyWidth="10%p"
152+
app:keyRole="tools"
153+
app:popupKeyboard="@xml/popup_tools_with_emoji_key"
154+
app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" />
152155
<Key
153156
app:code="-6"
154157
app:keyEdgeFlags="left"

app/src/main/res/xml/keys_letters_bengali.xml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,22 +191,25 @@
191191
app:keyWidth="15%p" />
192192
<Key
193193
app:keyLabel=","
194-
app:keyWidth="10%p" />
194+
app:keyWidth="10%p"
195+
app:keyRole="tools"
196+
app:popupKeyboard="@xml/popup_tools_with_emoji_key"
197+
app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" />
195198
<Key
196199
app:code="-6"
197200
app:keyEdgeFlags="left"
198201
app:keyIcon="@drawable/ic_emoji_emotions_outline_vector"
199-
app:secondaryKeyIcon="@drawable/ic_language_outlined"
200-
app:keyWidth="10%p" />
202+
app:keyWidth="10%p"
203+
app:secondaryKeyIcon="@drawable/ic_language_outlined" />
201204
<Key
202205
app:code="32"
203206
app:isRepeatable="true"
204207
app:keyWidth="40%p" />
205208
<Key
206209
app:keyLabel="."
210+
app:keyWidth="10%p"
207211
app:popupCharacters=",?!;:'…"
208-
app:popupKeyboard="@xml/keyboard_popup_template"
209-
app:keyWidth="10%p" />
212+
app:popupKeyboard="@xml/keyboard_popup_template" />
210213
<Key
211214
app:code="-4"
212215
app:keyEdgeFlags="right"

app/src/main/res/xml/keys_letters_bulgarian.xml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,22 +152,25 @@
152152
app:keyWidth="15%p" />
153153
<Key
154154
app:keyLabel=","
155-
app:keyWidth="10%p" />
155+
app:keyWidth="10%p"
156+
app:keyRole="tools"
157+
app:popupKeyboard="@xml/popup_tools_with_emoji_key"
158+
app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" />
156159
<Key
157160
app:code="-6"
158161
app:keyEdgeFlags="left"
159162
app:keyIcon="@drawable/ic_emoji_emotions_outline_vector"
160-
app:secondaryKeyIcon="@drawable/ic_language_outlined"
161-
app:keyWidth="10%p" />
163+
app:keyWidth="10%p"
164+
app:secondaryKeyIcon="@drawable/ic_language_outlined" />
162165
<Key
163166
app:code="32"
164167
app:isRepeatable="true"
165168
app:keyWidth="40%p" />
166169
<Key
167170
app:keyLabel="."
171+
app:keyWidth="10%p"
168172
app:popupCharacters=",?!;:'…"
169-
app:popupKeyboard="@xml/keyboard_popup_template"
170-
app:keyWidth="10%p" />
173+
app:popupKeyboard="@xml/keyboard_popup_template" />
171174
<Key
172175
app:code="-4"
173176
app:keyEdgeFlags="right"

app/src/main/res/xml/keys_letters_chuvash.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@
170170
app:keyWidth="15%p" />
171171
<Key
172172
app:keyLabel=","
173-
app:keyWidth="10%p" />
173+
app:keyWidth="10%p"
174+
app:keyRole="tools"
175+
app:popupKeyboard="@xml/popup_tools_with_emoji_key"
176+
app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" />
174177
<Key
175178
app:code="-6"
176179
app:keyEdgeFlags="left"

app/src/main/res/xml/keys_letters_czech_qwerty.xml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@
103103
app:keyLabel="d"
104104
app:popupCharacters="ď"
105105
app:popupKeyboard="@xml/keyboard_popup_template" />
106-
<Key app:keyLabel="f" />
106+
<Key app:keyLabel="f" />
107107
<Key app:keyLabel="g" />
108108
<Key app:keyLabel="h" />
109109
<Key app:keyLabel="j" />
110110
<Key app:keyLabel="k" />
111111
<Key
112112
app:keyEdgeFlags="right"
113-
app:keyLabel="l" />
113+
app:keyLabel="l" />
114114
</Row>
115115
<Row>
116116
<Key
@@ -149,22 +149,25 @@
149149
app:keyWidth="15%p" />
150150
<Key
151151
app:keyLabel=","
152-
app:keyWidth="10%p" />
152+
app:keyWidth="10%p"
153+
app:keyRole="tools"
154+
app:popupKeyboard="@xml/popup_tools_with_emoji_key"
155+
app:secondaryKeyIcon="@drawable/ic_emoji_emotions_outline_vector" />
153156
<Key
154157
app:code="-6"
155158
app:keyEdgeFlags="left"
156159
app:keyIcon="@drawable/ic_emoji_emotions_outline_vector"
157-
app:secondaryKeyIcon="@drawable/ic_language_outlined"
158-
app:keyWidth="10%p" />
160+
app:keyWidth="10%p"
161+
app:secondaryKeyIcon="@drawable/ic_language_outlined" />
159162
<Key
160163
app:code="32"
161164
app:isRepeatable="true"
162165
app:keyWidth="40%p" />
163166
<Key
164167
app:keyLabel="."
168+
app:keyWidth="10%p"
165169
app:popupCharacters=",?!;:'…"
166-
app:popupKeyboard="@xml/keyboard_popup_template"
167-
app:keyWidth="10%p" />
170+
app:popupKeyboard="@xml/keyboard_popup_template" />
168171
<Key
169172
app:code="-4"
170173
app:keyEdgeFlags="right"

0 commit comments

Comments
 (0)