@@ -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