Skip to content

Commit b4ebd18

Browse files
authored
Add mouseButton implementation on Android (#2680)
## Description This PR adds implementation of `mouseButton` prop on android. Since `actionButton` field is available only in API >= 23, we provide full support only for those versions. The only thing that changes for API < 23 is ignoring `ACTION_BUTTON_*`, because we don't want handlers to react to both type of events. Note that it requires [this PR](#2676) to work. ## Test plan Tested on `MouseButtons` example.
1 parent fa3af2e commit b4ebd18

File tree

7 files changed

+73
-7
lines changed

7 files changed

+73
-7
lines changed

android/src/main/java/com/swmansion/gesturehandler/core/FlingGestureHandler.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ class FlingGestureHandler : GestureHandler<FlingGestureHandler>() {
6767
}
6868

6969
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
70+
if (!shouldActivateWithMouse(sourceEvent)) {
71+
return
72+
}
73+
7074
val state = state
7175
if (state == STATE_UNDETERMINED) {
7276
startFling(sourceEvent)

android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.content.Context
55
import android.content.ContextWrapper
66
import android.graphics.PointF
77
import android.graphics.Rect
8+
import android.os.Build
89
import android.view.MotionEvent
910
import android.view.MotionEvent.PointerCoords
1011
import android.view.MotionEvent.PointerProperties
@@ -68,6 +69,8 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
6869
private var onTouchEventListener: OnTouchEventListener? = null
6970
private var interactionController: GestureHandlerInteractionController? = null
7071

72+
protected var mouseButton = 0
73+
7174
@Suppress("UNCHECKED_CAST")
7275
protected fun self(): ConcreteGestureHandlerT = this as ConcreteGestureHandlerT
7376

@@ -159,6 +162,10 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
159162
fun setInteractionController(controller: GestureHandlerInteractionController?): ConcreteGestureHandlerT =
160163
applySelf { interactionController = controller }
161164

165+
fun setMouseButton(mouseButton: Int) = apply {
166+
this.mouseButton = mouseButton
167+
}
168+
162169
fun prepare(view: View?, orchestrator: GestureHandlerOrchestrator?) {
163170
check(!(this.view != null || this.orchestrator != null)) { "Already prepared or hasn't been reset" }
164171
Arrays.fill(trackedPointerIDs, -1)
@@ -688,6 +695,46 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
688695
protected open fun onReset() {}
689696
protected open fun onCancel() {}
690697

698+
private fun isButtonInConfig(clickedButton: Int): Boolean {
699+
if (mouseButton == 0) {
700+
return clickedButton == MotionEvent.BUTTON_PRIMARY
701+
}
702+
703+
return clickedButton and mouseButton != 0
704+
}
705+
706+
protected fun shouldActivateWithMouse(sourceEvent: MotionEvent): Boolean {
707+
// While using mouse, we get both sets of events, for example ACTION_DOWN and ACTION_BUTTON_PRESS. That's why we want to take actions to only one of them.
708+
// On API >= 23, we will use events with infix BUTTON, otherwise we use standard action events (like ACTION_DOWN).
709+
710+
with(sourceEvent) {
711+
// To use actionButton, we need API >= 23.
712+
if (getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
713+
// While using mouse, we want to ignore default events for touch.
714+
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN) {
715+
return@shouldActivateWithMouse false
716+
}
717+
718+
// We don't want to do anything if wrong button was clicked. If we received event for BUTTON, we have to use actionButton to get which one was clicked.
719+
if (action != MotionEvent.ACTION_MOVE && !isButtonInConfig(actionButton)) {
720+
return@shouldActivateWithMouse false
721+
}
722+
723+
// When we receive ACTION_MOVE, we have to check buttonState field.
724+
if (action == MotionEvent.ACTION_MOVE && !isButtonInConfig(buttonState)) {
725+
return@shouldActivateWithMouse false
726+
}
727+
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
728+
// We do not fully support mouse below API 23, so we will ignore BUTTON events.
729+
if (action == MotionEvent.ACTION_BUTTON_PRESS || action == MotionEvent.ACTION_BUTTON_RELEASE) {
730+
return@shouldActivateWithMouse false
731+
}
732+
}
733+
}
734+
735+
return true
736+
}
737+
691738
/**
692739
* Transforms a point in the coordinate space of the wrapperView (GestureHandlerRootView) to
693740
* coordinate space of the view the gesture is attached to.

android/src/main/java/com/swmansion/gesturehandler/core/LongPressGestureHandler.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
3838
}
3939

4040
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
41+
if (!shouldActivateWithMouse(sourceEvent)) {
42+
return
43+
}
44+
4145
if (state == STATE_UNDETERMINED) {
4246
previousTime = SystemClock.uptimeMillis()
4347
startTime = previousTime
@@ -51,7 +55,7 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
5155
activate()
5256
}
5357
}
54-
if (sourceEvent.actionMasked == MotionEvent.ACTION_UP) {
58+
if (sourceEvent.actionMasked == MotionEvent.ACTION_UP || sourceEvent.actionMasked == MotionEvent.ACTION_BUTTON_RELEASE) {
5559
handler?.let {
5660
it.removeCallbacksAndMessages(null)
5761
handler = null

android/src/main/java/com/swmansion/gesturehandler/core/PanGestureHandler.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
208208
}
209209

210210
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
211+
if (!shouldActivateWithMouse(sourceEvent)) {
212+
return
213+
}
214+
211215
val state = state
212216
val action = sourceEvent.actionMasked
213217
if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN) {
@@ -246,7 +250,7 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
246250
velocityX = velocityTracker!!.xVelocity
247251
velocityY = velocityTracker!!.yVelocity
248252
}
249-
if (action == MotionEvent.ACTION_UP) {
253+
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_BUTTON_RELEASE) {
250254
if (state == STATE_ACTIVE) {
251255
end()
252256
} else {

android/src/main/java/com/swmansion/gesturehandler/core/TapGestureHandler.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ class TapGestureHandler : GestureHandler<TapGestureHandler>() {
105105
}
106106

107107
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
108+
if (!shouldActivateWithMouse(sourceEvent)) {
109+
return
110+
}
111+
108112
val state = state
109113
val action = sourceEvent.actionMasked
110114
if (state == STATE_UNDETERMINED) {
@@ -130,14 +134,14 @@ class TapGestureHandler : GestureHandler<TapGestureHandler>() {
130134
if (shouldFail()) {
131135
fail()
132136
} else if (state == STATE_UNDETERMINED) {
133-
if (action == MotionEvent.ACTION_DOWN) {
137+
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_BUTTON_PRESS) {
134138
begin()
135139
}
136140
startTap()
137141
} else if (state == STATE_BEGAN) {
138-
if (action == MotionEvent.ACTION_UP) {
142+
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_BUTTON_RELEASE) {
139143
endTap()
140-
} else if (action == MotionEvent.ACTION_DOWN) {
144+
} else if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_BUTTON_PRESS) {
141145
startTap()
142146
}
143147
}

android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
7171
if (config.hasKey(KEY_MANUAL_ACTIVATION)) {
7272
handler.setManualActivation(config.getBoolean(KEY_MANUAL_ACTIVATION))
7373
}
74+
if (config.hasKey("mouseButton")) {
75+
handler.setMouseButton(config.getInt("mouseButton"))
76+
}
7477
}
7578

7679
abstract fun createEventBuilder(handler: T): GestureHandlerEventDataBuilder<T>

src/web/interfaces.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ export interface AdaptedEvent {
133133

134134
export enum MouseButton {
135135
LEFT = 1,
136-
MIDDLE = 2,
137-
RIGHT = 4,
136+
RIGHT = 2,
137+
MIDDLE = 4,
138138
BUTTON_4 = 8,
139139
BUTTON_5 = 16,
140140
ALL = 31,

0 commit comments

Comments
 (0)