Skip to content

Commit 8eca53c

Browse files
committed
RELEASE build, fixed fast typing (wultiple touch events)
1 parent 2cea4e4 commit 8eca53c

5 files changed

Lines changed: 105 additions & 26 deletions

File tree

app/build.gradle.kts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,28 @@ plugins {
44
id("org.jetbrains.kotlin.plugin.serialization")
55
}
66
android {
7+
signingConfigs {
8+
create("release") {
9+
}
10+
}
711
namespace = "com.roalyr.customkeyboardengine"
812
compileSdk = 34
913

1014
defaultConfig {
1115
applicationId = "com.roalyr.customkeyboardengine"
1216
minSdk = 26
1317
targetSdk = 34
14-
versionCode = 1
15-
versionName = "1.2"
18+
versionCode = 2
19+
versionName = "1.2.1"
1620

1721
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
1822
vectorDrawables {
1923
useSupportLibrary = true
2024
}
25+
versionNameSuffix = "-beta"
2126
}
2227

28+
2329
buildTypes {
2430
release {
2531
isMinifyEnabled = false
9.39 KB
Binary file not shown.
9.34 KB
Binary file not shown.

app/release/output-metadata.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"version": 3,
3+
"artifactType": {
4+
"type": "APK",
5+
"kind": "Directory"
6+
},
7+
"applicationId": "com.roalyr.customkeyboardengine",
8+
"variantName": "release",
9+
"elements": [
10+
{
11+
"type": "SINGLE",
12+
"filters": [],
13+
"attributes": [],
14+
"versionCode": 2,
15+
"versionName": "1.2.1-beta",
16+
"outputFile": "app-release.apk"
17+
}
18+
],
19+
"elementType": "File",
20+
"baselineProfiles": [
21+
{
22+
"minApi": 28,
23+
"maxApi": 30,
24+
"baselineProfiles": [
25+
"baselineProfiles/1/app-release.dm"
26+
]
27+
},
28+
{
29+
"minApi": 31,
30+
"maxApi": 2147483647,
31+
"baselineProfiles": [
32+
"baselineProfiles/0/app-release.dm"
33+
]
34+
}
35+
],
36+
"minSdkVersionForDexing": 26
37+
}

app/src/main/kotlin/com/roalyr/customkeyboardengine/CustomKeyboardView.kt

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ class CustomKeyboardView @JvmOverloads constructor(
6060
private var isKeyRepeated = false
6161
private var isLongPressHandled = false
6262
private var downKeyIndex = -1
63-
private var currentKey: Key? = null
63+
6464
private var repeatKeyRunnable: Runnable? = null
6565

66+
private val activeKeys = mutableMapOf<Int, Key?>() // Track active keys by pointer ID
67+
68+
6669
companion object {
6770
private const val TAG = "CustomKeyboardView"
6871
private const val MSG_LONGPRESS = 1
@@ -71,7 +74,6 @@ class CustomKeyboardView @JvmOverloads constructor(
7174
private const val REPEAT_START_DELAY = 250
7275
}
7376

74-
///////////////////////////////////
7577
// INIT
7678
init {
7779
isFocusable = true
@@ -87,10 +89,7 @@ class CustomKeyboardView @JvmOverloads constructor(
8789
invalidateAllKeys()
8890
}
8991

90-
91-
/////////////////////////////////////
9292
// Handle events
93-
9493
private val handlerCallback = Handler.Callback { msg ->
9594
if (msg.what == MSG_LONGPRESS) {
9695
val key = msg.obj as? Key
@@ -105,7 +104,7 @@ class CustomKeyboardView @JvmOverloads constructor(
105104

106105
private val handler = Handler(Looper.getMainLooper(), handlerCallback)
107106

108-
private fun handleTouchDown(x: Int, y: Int) {
107+
private fun handleTouchDown(x: Int, y: Int, pointerId: Int) {
109108
val key = keyboard?.getKeyAt(x.toFloat(), y.toFloat()) ?: return
110109

111110
downKeyIndex = keys?.indexOf(key) ?: -1
@@ -120,35 +119,40 @@ class CustomKeyboardView @JvmOverloads constructor(
120119
scheduleLongPress(key)
121120
}
122121

123-
// Save key reference for later use in handleTouchUp
124-
currentKey = key
122+
// Save key reference in the activeKeys map
123+
activeKeys[pointerId] = key
125124
}
126125

127-
private fun handleTouchUp(x: Int, y: Int) {
128-
// Cancel repeat and long press behavior
126+
127+
private fun handleTouchUp(x: Int, y: Int, pointerId: Int) {
129128
cancelRepeatKey()
130129
handler.removeMessages(MSG_LONGPRESS)
131130

132-
currentKey?.let { key ->
131+
// Retrieve the key associated with this pointer ID
132+
val key = activeKeys[pointerId]
133+
134+
key?.let {
133135
when {
134136
isLongPressHandled -> {
135-
// Skip short press if long press is already handled
137+
return
136138
}
137139
isKeyRepeated -> {
138-
// Skip short press if key repeat is already handled
140+
return
139141
}
140142
else -> {
141-
keyboardActionListener?.onKey(key.keyCode, key.label)
143+
keyboardActionListener?.onKey(it.keyCode, it.label)
142144
}
143145
}
144146
}
145147

146-
// Reset states
148+
// Reset states for this pointer
147149
isLongPressHandled = false
148150
isKeyRepeated = false
149-
currentKey = null
151+
activeKeys.remove(pointerId)
150152
}
151153

154+
155+
152156
private fun handleLongPress(key: Key) {
153157
isLongPressHandled = true
154158
keyboardActionListener?.onKey(key.keyCodeLongPress, key.smallLabel)
@@ -203,22 +207,54 @@ class CustomKeyboardView @JvmOverloads constructor(
203207
}
204208

205209
override fun onTouchEvent(event: MotionEvent): Boolean {
206-
val action = event.action
207-
val scaledX = (event.x / scaleX).toInt()
208-
val scaledY = (event.y / scaleY).toInt()
210+
val actionMasked = event.actionMasked // Use actionMasked for multi-touch support
211+
val pointerIndex = event.actionIndex // Index of the pointer causing the event
212+
val pointerId = event.getPointerId(pointerIndex)
213+
214+
when (actionMasked) {
215+
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
216+
// Handle new touch
217+
val scaledX = (event.getX(pointerIndex) / scaleX).toInt()
218+
val scaledY = (event.getY(pointerIndex) / scaleY).toInt()
219+
handleTouchDown(scaledX, scaledY, pointerId)
220+
performClick()
221+
//Log.d("TOUCH", "Pointer Down: Index $pointerIndex, x=$scaledX, y=$scaledY")
222+
}
209223

210-
when (action) {
211-
MotionEvent.ACTION_DOWN -> {
212-
handleTouchDown(scaledX, scaledY)
224+
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
225+
// Handle touch release
226+
val scaledX = (event.getX(pointerIndex) / scaleX).toInt()
227+
val scaledY = (event.getY(pointerIndex) / scaleY).toInt()
228+
handleTouchUp(scaledX, scaledY, pointerId)
213229
performClick()
230+
//Log.d("TOUCH", "Pointer Up: Index $pointerIndex, x=$scaledX, y=$scaledY")
214231
}
215232

216-
MotionEvent.ACTION_MOVE -> handleTouchMove(scaledX, scaledY)
217-
MotionEvent.ACTION_UP -> handleTouchUp(scaledX, scaledY)
233+
MotionEvent.ACTION_MOVE -> {
234+
// Handle touch move for all active pointers
235+
//for (i in 0 until event.pointerCount) {
236+
//val scaledX = (event.getX(i) / scaleX).toInt()
237+
//val scaledY = (event.getY(i) / scaleY).toInt()
238+
//handleTouchMove(scaledX, scaledY)
239+
//Log.d("TOUCH", "Pointer Move: Index $i, x=$scaledX, y=$scaledY")
240+
//}
241+
}
242+
243+
MotionEvent.ACTION_CANCEL -> {
244+
// Handle cancel action if necessary
245+
//Log.d("TOUCH", "Action Cancel")
246+
}
218247
}
219248
return true
220249
}
221250

251+
252+
override fun performClick(): Boolean {
253+
// Call the superclass implementation (important for accessibility events)
254+
super.performClick()
255+
return true
256+
}
257+
222258
private fun handleTouchMove(x: Int, y: Int) {
223259
// Optional: Cancel repeat/long press if the finger moves off the key
224260
if (keyboard?.getKeyAt(x.toFloat(), y.toFloat()) == null) {

0 commit comments

Comments
 (0)