react-native-gesture-handler 2.4.2 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandler.kt +9 -5
- package/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.kt +6 -1
- package/android/lib/src/main/java/com/swmansion/gesturehandler/NativeViewGestureHandler.kt +103 -22
- package/android/lib/src/main/java/com/swmansion/gesturehandler/PanGestureHandler.kt +29 -2
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +74 -84
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +4 -0
- package/android/src/main/jni/Android.mk +1 -2
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerDelegate.java +12 -9
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerInterface.java +1 -0
- package/ios/Handlers/RNFlingHandler.m +43 -1
- package/ios/Handlers/{RNNativeViewHandler.m → RNNativeViewHandler.mm} +13 -1
- package/ios/Handlers/RNPanHandler.m +27 -0
- package/ios/RNGestureHandler.h +1 -0
- package/ios/RNGestureHandler.m +22 -4
- package/ios/RNGestureHandlerManager.mm +10 -2
- package/ios/RNGestureHandlerModule.mm +4 -1
- package/ios/RNManualActivationRecognizer.m +10 -3
- package/ios/RNRootViewGestureRecognizer.m +12 -1
- package/lib/commonjs/RNGestureHandlerModule.macos.js +81 -0
- package/lib/commonjs/RNGestureHandlerModule.macos.js.map +1 -0
- package/lib/commonjs/components/GestureButtons.js.map +1 -1
- package/lib/commonjs/components/touchables/GenericTouchable.js +4 -1
- package/lib/commonjs/components/touchables/GenericTouchable.js.map +1 -1
- package/lib/commonjs/fabric/RNGestureHandlerButtonNativeComponent.js.map +1 -1
- package/lib/commonjs/handlers/PanGestureHandler.js +1 -1
- package/lib/commonjs/handlers/PanGestureHandler.js.map +1 -1
- package/lib/commonjs/handlers/PressabilityDebugView.js +14 -0
- package/lib/commonjs/handlers/PressabilityDebugView.js.map +1 -0
- package/lib/commonjs/handlers/PressabilityDebugView.web.js +12 -0
- package/lib/commonjs/handlers/PressabilityDebugView.web.js.map +1 -0
- package/lib/commonjs/handlers/createHandler.js +6 -3
- package/lib/commonjs/handlers/createHandler.js.map +1 -1
- package/lib/commonjs/handlers/gestures/gestureStateManager.js +13 -9
- package/lib/commonjs/handlers/gestures/gestureStateManager.js.map +1 -1
- package/lib/commonjs/handlers/gestures/panGesture.js +5 -0
- package/lib/commonjs/handlers/gestures/panGesture.js.map +1 -1
- package/lib/commonjs/utils.js +6 -3
- package/lib/commonjs/utils.js.map +1 -1
- package/lib/module/RNGestureHandlerModule.macos.js +57 -0
- package/lib/module/RNGestureHandlerModule.macos.js.map +1 -0
- package/lib/module/components/GestureButtons.js.map +1 -1
- package/lib/module/components/touchables/GenericTouchable.js +4 -1
- package/lib/module/components/touchables/GenericTouchable.js.map +1 -1
- package/lib/module/fabric/RNGestureHandlerButtonNativeComponent.js.map +1 -1
- package/lib/module/handlers/PanGestureHandler.js +1 -1
- package/lib/module/handlers/PanGestureHandler.js.map +1 -1
- package/lib/module/handlers/PressabilityDebugView.js +3 -0
- package/lib/module/handlers/PressabilityDebugView.js.map +1 -0
- package/lib/module/handlers/PressabilityDebugView.web.js +5 -0
- package/lib/module/handlers/PressabilityDebugView.web.js.map +1 -0
- package/lib/module/handlers/createHandler.js +6 -4
- package/lib/module/handlers/createHandler.js.map +1 -1
- package/lib/module/handlers/gestures/gestureStateManager.js +13 -9
- package/lib/module/handlers/gestures/gestureStateManager.js.map +1 -1
- package/lib/module/handlers/gestures/panGesture.js +5 -0
- package/lib/module/handlers/gestures/panGesture.js.map +1 -1
- package/lib/module/utils.js +2 -1
- package/lib/module/utils.js.map +1 -1
- package/lib/typescript/RNGestureHandlerModule.macos.d.ts +34 -0
- package/lib/typescript/RNGestureHandlerModule.web.d.ts +1 -1
- package/lib/typescript/components/GestureButtons.d.ts +6 -0
- package/lib/typescript/fabric/RNGestureHandlerButtonNativeComponent.d.ts +1 -0
- package/lib/typescript/handlers/PanGestureHandler.d.ts +2 -1
- package/lib/typescript/handlers/PressabilityDebugView.d.ts +1 -0
- package/lib/typescript/handlers/PressabilityDebugView.web.d.ts +1 -0
- package/lib/typescript/handlers/gestures/panGesture.d.ts +1 -0
- package/lib/typescript/web/NodeManager.d.ts +2 -2
- package/package.json +1 -1
- package/src/RNGestureHandlerModule.macos.ts +62 -0
- package/src/components/GestureButtons.tsx +7 -0
- package/src/components/touchables/GenericTouchable.tsx +1 -0
- package/src/fabric/RNGestureHandlerButtonNativeComponent.ts +1 -0
- package/src/handlers/PanGestureHandler.ts +2 -0
- package/src/handlers/PressabilityDebugView.tsx +2 -0
- package/src/handlers/PressabilityDebugView.web.tsx +4 -0
- package/src/handlers/{createHandler.ts → createHandler.tsx} +7 -6
- package/src/handlers/gestures/gestureStateManager.ts +13 -8
- package/src/handlers/gestures/panGesture.ts +5 -0
- package/src/utils.ts +3 -1
- package/ios/RNGestureHandler.xcodeproj/project.xcworkspace/xcuserdata/jakubpiasecki.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/RNGestureHandler.xcodeproj/xcuserdata/jakubpiasecki.xcuserdatad/xcschemes/xcschememanagement.plist +0 -19
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Check out our dedicated documentation page for info about this library, API refe
|
|
|
24
24
|
If you want to play with the API but don't feel like trying it on a real app, you can run the example project. Clone the repo, go to the `example` folder and run:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
27
|
+
yarn install
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
If you are running on ios, run `pod install` in the ios folder
|
|
@@ -44,7 +44,8 @@ You will need to have an Android or iOS device or emulator connected as well as
|
|
|
44
44
|
| <1.1.0 | 0.50.0+ |
|
|
45
45
|
|
|
46
46
|
It may be possible to use newer versions of react-native-gesture-handler on React Native with version <= 0.59 by reverse Jetifying.
|
|
47
|
-
Read more on that here https://github.com/mikehardy/jetifier#to-reverse-jetify--convert-node_modules-dependencies-to-support-libraries
|
|
47
|
+
Read more on that here <https://github.com/mikehardy/jetifier#to-reverse-jetify--convert-node_modules-dependencies-to-support-libraries>
|
|
48
|
+
|
|
48
49
|
## License
|
|
49
50
|
|
|
50
51
|
Gesture handler library is licensed under [The MIT License](LICENSE).
|
|
@@ -172,8 +172,12 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
|
|
|
172
172
|
windowOffset[0] = 0
|
|
173
173
|
windowOffset[1] = 0
|
|
174
174
|
}
|
|
175
|
+
|
|
176
|
+
onPrepare()
|
|
175
177
|
}
|
|
176
178
|
|
|
179
|
+
protected open fun onPrepare() {}
|
|
180
|
+
|
|
177
181
|
private fun getWindow(context: Context?): Window? {
|
|
178
182
|
if (context == null) return null
|
|
179
183
|
if (context is Activity) return context.window
|
|
@@ -253,9 +257,9 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
|
|
|
253
257
|
}
|
|
254
258
|
initPointerProps(trackedPointersIDsCount)
|
|
255
259
|
var count = 0
|
|
256
|
-
val
|
|
257
|
-
val
|
|
258
|
-
event.
|
|
260
|
+
val deltaX = event.rawX - event.x
|
|
261
|
+
val deltaY = event.rawY - event.y
|
|
262
|
+
event.offsetLocation(deltaX, deltaY)
|
|
259
263
|
var index = 0
|
|
260
264
|
val size = event.pointerCount
|
|
261
265
|
while (index < size) {
|
|
@@ -298,8 +302,8 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
|
|
|
298
302
|
} catch (e: IllegalArgumentException) {
|
|
299
303
|
throw AdaptEventException(this, event, e)
|
|
300
304
|
}
|
|
301
|
-
event.
|
|
302
|
-
result.
|
|
305
|
+
event.offsetLocation(-deltaX, -deltaY)
|
|
306
|
+
result.offsetLocation(-deltaX, -deltaY)
|
|
303
307
|
return result
|
|
304
308
|
}
|
|
305
309
|
|
package/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.kt
CHANGED
|
@@ -475,7 +475,12 @@ class GestureHandlerOrchestrator(
|
|
|
475
475
|
PointerEventsConfig.BOX_NONE -> {
|
|
476
476
|
// This view can't be the target, but its children might
|
|
477
477
|
if (view is ViewGroup) {
|
|
478
|
-
extractGestureHandlers(view, coords, pointerId)
|
|
478
|
+
extractGestureHandlers(view, coords, pointerId).also { found ->
|
|
479
|
+
// A child view is handling touch, also extract handlers attached to this view
|
|
480
|
+
if (found) {
|
|
481
|
+
recordViewHandlersForPointer(view, coords, pointerId)
|
|
482
|
+
}
|
|
483
|
+
}
|
|
479
484
|
} else false
|
|
480
485
|
}
|
|
481
486
|
PointerEventsConfig.AUTO -> {
|
|
@@ -3,13 +3,16 @@ package com.swmansion.gesturehandler
|
|
|
3
3
|
import android.os.SystemClock
|
|
4
4
|
import android.view.MotionEvent
|
|
5
5
|
import android.view.View
|
|
6
|
+
import android.view.ViewConfiguration
|
|
6
7
|
import android.view.ViewGroup
|
|
7
|
-
import com.
|
|
8
|
+
import com.facebook.react.views.textinput.ReactEditText
|
|
8
9
|
|
|
9
10
|
class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
|
|
10
11
|
private var shouldActivateOnStart = false
|
|
11
12
|
private var disallowInterruption = false
|
|
12
13
|
|
|
14
|
+
private var hook: NativeViewGestureHandlerHook = defaultHook
|
|
15
|
+
|
|
13
16
|
init {
|
|
14
17
|
setShouldCancelWhenOutside(true)
|
|
15
18
|
}
|
|
@@ -34,13 +37,17 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
override fun shouldRecognizeSimultaneously(handler: GestureHandler<*>): Boolean {
|
|
40
|
+
// if the gesture is marked by user as simultaneous with other or the hook return true
|
|
41
|
+
if (super.shouldRecognizeSimultaneously(handler) || hook.shouldRecognizeSimultaneously(handler)) {
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
if (handler is NativeViewGestureHandler) {
|
|
38
46
|
// Special case when the peer handler is also an instance of NativeViewGestureHandler:
|
|
39
47
|
// For the `disallowInterruption` to work correctly we need to check the property when
|
|
40
48
|
// accessed as a peer, because simultaneous recognizers can be set on either side of the
|
|
41
49
|
// connection.
|
|
42
|
-
|
|
43
|
-
if (nativeWrapper.state == STATE_ACTIVE && nativeWrapper.disallowInterruption) {
|
|
50
|
+
if (handler.state == STATE_ACTIVE && handler.disallowInterruption) {
|
|
44
51
|
// other handler is active and it disallows interruption, we don't want to get into its way
|
|
45
52
|
return false
|
|
46
53
|
}
|
|
@@ -52,7 +59,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
|
|
|
52
59
|
// as it means the other handler has turned active and returning `true` would prevent it from
|
|
53
60
|
// interrupting the current handler
|
|
54
61
|
false
|
|
55
|
-
} else state == STATE_ACTIVE && canBeInterrupted
|
|
62
|
+
} else state == STATE_ACTIVE && canBeInterrupted && (!hook.shouldCancelRootViewGestureHandlerIfNecessary() || handler.tag > 0)
|
|
56
63
|
// otherwise we can only return `true` if already in an active state
|
|
57
64
|
}
|
|
58
65
|
|
|
@@ -60,19 +67,10 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
|
|
|
60
67
|
return !disallowInterruption
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
val view = view
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return true
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private fun afterGestureEnd() {
|
|
73
|
-
val view = view
|
|
74
|
-
if (view is StateChangeHook) {
|
|
75
|
-
view.afterGestureEnd()
|
|
70
|
+
override fun onPrepare() {
|
|
71
|
+
when (val view = view) {
|
|
72
|
+
is NativeViewGestureHandlerHook -> this.hook = view
|
|
73
|
+
is ReactEditText -> this.hook = EditTextHook(this, view)
|
|
76
74
|
}
|
|
77
75
|
}
|
|
78
76
|
|
|
@@ -84,7 +82,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
|
|
|
84
82
|
activate()
|
|
85
83
|
}
|
|
86
84
|
end()
|
|
87
|
-
afterGestureEnd()
|
|
85
|
+
hook.afterGestureEnd(event)
|
|
88
86
|
} else if (state == STATE_UNDETERMINED || state == STATE_BEGAN) {
|
|
89
87
|
when {
|
|
90
88
|
shouldActivateOnStart -> {
|
|
@@ -96,8 +94,11 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
|
|
|
96
94
|
view.onTouchEvent(event)
|
|
97
95
|
activate()
|
|
98
96
|
}
|
|
97
|
+
hook.wantsToHandleEventBeforeActivation() -> {
|
|
98
|
+
hook.handleEventBeforeActivation(event)
|
|
99
|
+
}
|
|
99
100
|
state != STATE_BEGAN -> {
|
|
100
|
-
if (
|
|
101
|
+
if (hook.canBegin()) {
|
|
101
102
|
begin()
|
|
102
103
|
} else {
|
|
103
104
|
cancel()
|
|
@@ -117,13 +118,93 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
|
|
|
117
118
|
view!!.onTouchEvent(event)
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
override fun onReset() {
|
|
122
|
+
this.hook = defaultHook
|
|
123
|
+
}
|
|
124
|
+
|
|
120
125
|
companion object {
|
|
121
126
|
private fun tryIntercept(view: View, event: MotionEvent) =
|
|
122
127
|
view is ViewGroup && view.onInterceptTouchEvent(event)
|
|
128
|
+
|
|
129
|
+
private val defaultHook = object : NativeViewGestureHandlerHook {}
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
interface
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
interface NativeViewGestureHandlerHook {
|
|
133
|
+
/**
|
|
134
|
+
* Called when gesture is in the UNDETERMINED state, shouldActivateOnStart is set to false,
|
|
135
|
+
* and both tryIntercept and wantsToHandleEventBeforeActivation returned false.
|
|
136
|
+
*
|
|
137
|
+
* @return Boolean value signalling whether the handler can transition to the BEGAN state. If false
|
|
138
|
+
* the gesture will be cancelled.
|
|
139
|
+
*/
|
|
140
|
+
fun canBegin() = true
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Called after the gesture transitions to the END state.
|
|
144
|
+
*/
|
|
145
|
+
fun afterGestureEnd(event: MotionEvent) = Unit
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @return Boolean value signalling whether the gesture can be recognized simultaneously with
|
|
149
|
+
* other (handler). Returning false doesn't necessarily prevent it from happening.
|
|
150
|
+
*/
|
|
151
|
+
fun shouldRecognizeSimultaneously(handler: GestureHandler<*>) = false
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* shouldActivateOnStart and tryIntercept have priority over this method
|
|
155
|
+
*
|
|
156
|
+
* @return Boolean value signalling if the hook wants to handle events passed to the handler
|
|
157
|
+
* before it activates (after that the events are passed to the underlying view).
|
|
158
|
+
*/
|
|
159
|
+
fun wantsToHandleEventBeforeActivation() = false
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Will be called with events if wantsToHandleEventBeforeActivation returns true.
|
|
163
|
+
*/
|
|
164
|
+
fun handleEventBeforeActivation(event: MotionEvent) = Unit
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @return Boolean value indicating whether the RootViewGestureHandler should be cancelled
|
|
168
|
+
* by this one.
|
|
169
|
+
*/
|
|
170
|
+
fun shouldCancelRootViewGestureHandlerIfNecessary() = false
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private class EditTextHook(
|
|
174
|
+
private val handler: NativeViewGestureHandler,
|
|
175
|
+
private val editText: ReactEditText
|
|
176
|
+
) : NativeViewGestureHandlerHook {
|
|
177
|
+
private var startX = 0f
|
|
178
|
+
private var startY = 0f
|
|
179
|
+
private var touchSlopSquared: Int
|
|
180
|
+
|
|
181
|
+
init {
|
|
182
|
+
val vc = ViewConfiguration.get(editText.context)
|
|
183
|
+
touchSlopSquared = vc.scaledTouchSlop * vc.scaledTouchSlop
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
override fun afterGestureEnd(event: MotionEvent) {
|
|
187
|
+
if ((event.x - startX) * (event.x - startX) + (event.y - startY) * (event.y - startY) < touchSlopSquared) {
|
|
188
|
+
editText.requestFocusFromJS()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// recognize alongside every handler besides RootViewGestureHandler, which is a private inner class
|
|
193
|
+
// of RNGestureHandlerRootHelper so no explicit type checks, but its tag is always negative
|
|
194
|
+
// also if other handler is NativeViewGestureHandler then don't override the default implementation
|
|
195
|
+
override fun shouldRecognizeSimultaneously(handler: GestureHandler<*>) =
|
|
196
|
+
handler.tag > 0 && handler !is NativeViewGestureHandler
|
|
197
|
+
|
|
198
|
+
override fun wantsToHandleEventBeforeActivation() = true
|
|
199
|
+
|
|
200
|
+
override fun handleEventBeforeActivation(event: MotionEvent) {
|
|
201
|
+
handler.activate()
|
|
202
|
+
editText.onTouchEvent(event)
|
|
203
|
+
|
|
204
|
+
startX = event.x
|
|
205
|
+
startY = event.y
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
override fun shouldCancelRootViewGestureHandlerIfNecessary() = true
|
|
128
209
|
}
|
|
129
210
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.swmansion.gesturehandler
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.os.Handler
|
|
4
5
|
import android.view.MotionEvent
|
|
5
6
|
import android.view.VelocityTracker
|
|
6
7
|
import android.view.ViewConfiguration
|
|
@@ -40,6 +41,9 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
40
41
|
private var lastY = 0f
|
|
41
42
|
private var velocityTracker: VelocityTracker? = null
|
|
42
43
|
private var averageTouches = false
|
|
44
|
+
private var activateAfterLongPress = DEFAULT_ACTIVATE_AFTER_LONG_PRESS
|
|
45
|
+
private val activateDelayed = Runnable { activate() }
|
|
46
|
+
private var handler: Handler? = null
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
49
|
* On Android when there are multiple pointers on the screen pan gestures most often just consider
|
|
@@ -54,7 +58,7 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
54
58
|
* position of all the fingers will remain still while doing a rotation gesture.
|
|
55
59
|
*/
|
|
56
60
|
init {
|
|
57
|
-
val vc = ViewConfiguration.get(context)
|
|
61
|
+
val vc = ViewConfiguration.get(context!!)
|
|
58
62
|
val touchSlop = vc.scaledTouchSlop
|
|
59
63
|
defaultMinDistSq = (touchSlop * touchSlop).toFloat()
|
|
60
64
|
minDistSq = defaultMinDistSq
|
|
@@ -76,6 +80,7 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
76
80
|
minDistSq = defaultMinDistSq
|
|
77
81
|
minPointers = DEFAULT_MIN_POINTERS
|
|
78
82
|
maxPointers = DEFAULT_MAX_POINTERS
|
|
83
|
+
activateAfterLongPress = DEFAULT_ACTIVATE_AFTER_LONG_PRESS
|
|
79
84
|
averageTouches = false
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -127,6 +132,10 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
127
132
|
this.averageTouches = averageTouches
|
|
128
133
|
}
|
|
129
134
|
|
|
135
|
+
fun setActivateAfterLongPress(time: Long) = apply {
|
|
136
|
+
this.activateAfterLongPress = time
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
/**
|
|
131
140
|
* @param minVelocity in pixels per second
|
|
132
141
|
*/
|
|
@@ -177,13 +186,18 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
177
186
|
|
|
178
187
|
private fun shouldFail(): Boolean {
|
|
179
188
|
val dx = lastX - startX + offsetX
|
|
189
|
+
val dy = lastY - startY + offsetY
|
|
190
|
+
|
|
191
|
+
if (activateAfterLongPress > 0 && dx * dx + dy * dy > defaultMinDistSq) {
|
|
192
|
+
handler?.removeCallbacksAndMessages(null)
|
|
193
|
+
return true
|
|
194
|
+
}
|
|
180
195
|
if (failOffsetXStart != MAX_VALUE_IGNORE && dx < failOffsetXStart) {
|
|
181
196
|
return true
|
|
182
197
|
}
|
|
183
198
|
if (failOffsetXEnd != MIN_VALUE_IGNORE && dx > failOffsetXEnd) {
|
|
184
199
|
return true
|
|
185
200
|
}
|
|
186
|
-
val dy = lastY - startY + offsetY
|
|
187
201
|
if (failOffsetYStart != MAX_VALUE_IGNORE && dy < failOffsetYStart) {
|
|
188
202
|
return true
|
|
189
203
|
}
|
|
@@ -216,6 +230,13 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
216
230
|
velocityTracker = VelocityTracker.obtain()
|
|
217
231
|
addVelocityMovement(velocityTracker, event)
|
|
218
232
|
begin()
|
|
233
|
+
|
|
234
|
+
if (activateAfterLongPress > 0) {
|
|
235
|
+
if (handler == null) {
|
|
236
|
+
handler = Handler()
|
|
237
|
+
}
|
|
238
|
+
handler!!.postDelayed(activateDelayed, activateAfterLongPress)
|
|
239
|
+
}
|
|
219
240
|
} else if (velocityTracker != null) {
|
|
220
241
|
addVelocityMovement(velocityTracker, event)
|
|
221
242
|
velocityTracker!!.computeCurrentVelocity(1000)
|
|
@@ -257,7 +278,12 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
257
278
|
super.activate(force)
|
|
258
279
|
}
|
|
259
280
|
|
|
281
|
+
override fun onCancel() {
|
|
282
|
+
handler?.removeCallbacksAndMessages(null)
|
|
283
|
+
}
|
|
284
|
+
|
|
260
285
|
override fun onReset() {
|
|
286
|
+
handler?.removeCallbacksAndMessages(null)
|
|
261
287
|
velocityTracker?.let {
|
|
262
288
|
it.recycle()
|
|
263
289
|
velocityTracker = null
|
|
@@ -274,6 +300,7 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
|
|
|
274
300
|
private const val MAX_VALUE_IGNORE = Float.MIN_VALUE
|
|
275
301
|
private const val DEFAULT_MIN_POINTERS = 1
|
|
276
302
|
private const val DEFAULT_MAX_POINTERS = 10
|
|
303
|
+
private const val DEFAULT_ACTIVATE_AFTER_LONG_PRESS = 0L
|
|
277
304
|
|
|
278
305
|
/**
|
|
279
306
|
* This method adds movement to {@class VelocityTracker} first resetting offset of the event so
|
|
@@ -9,6 +9,8 @@ import android.graphics.drawable.Drawable
|
|
|
9
9
|
import android.graphics.drawable.LayerDrawable
|
|
10
10
|
import android.graphics.drawable.PaintDrawable
|
|
11
11
|
import android.graphics.drawable.RippleDrawable
|
|
12
|
+
import android.graphics.drawable.ShapeDrawable
|
|
13
|
+
import android.graphics.drawable.shapes.RectShape
|
|
12
14
|
import android.os.Build
|
|
13
15
|
import android.util.TypedValue
|
|
14
16
|
import android.view.MotionEvent
|
|
@@ -16,7 +18,6 @@ import android.view.View
|
|
|
16
18
|
import android.view.View.OnClickListener
|
|
17
19
|
import android.view.ViewGroup
|
|
18
20
|
import androidx.core.view.children
|
|
19
|
-
import com.facebook.react.bridge.SoftAssertions
|
|
20
21
|
import com.facebook.react.module.annotations.ReactModule
|
|
21
22
|
import com.facebook.react.uimanager.PixelUtil
|
|
22
23
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
@@ -77,6 +78,11 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
77
78
|
view.exclusive = exclusive
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
@ReactProp(name = "touchSoundDisabled")
|
|
82
|
+
override fun setTouchSoundDisabled(view: ButtonViewGroup, touchSoundDisabled: Boolean) {
|
|
83
|
+
view.isSoundEffectsEnabled = !touchSoundDisabled
|
|
84
|
+
}
|
|
85
|
+
|
|
80
86
|
override fun onAfterUpdateTransaction(view: ButtonViewGroup) {
|
|
81
87
|
view.updateBackground()
|
|
82
88
|
}
|
|
@@ -86,7 +92,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
class ButtonViewGroup(context: Context?) : ViewGroup(context),
|
|
89
|
-
NativeViewGestureHandler.
|
|
95
|
+
NativeViewGestureHandler.NativeViewGestureHandlerHook {
|
|
90
96
|
// Using object because of handling null representing no value set.
|
|
91
97
|
var rippleColor: Int? = null
|
|
92
98
|
set(color) = withBackgroundUpdate {
|
|
@@ -132,30 +138,6 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
132
138
|
_backgroundColor = color
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
private fun applyRippleEffectWhenNeeded(selectable: Drawable): Drawable {
|
|
136
|
-
val rippleColor = rippleColor
|
|
137
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && selectable is RippleDrawable) {
|
|
138
|
-
val states = arrayOf(intArrayOf(android.R.attr.state_enabled))
|
|
139
|
-
val colorStateList = if (rippleColor != null) {
|
|
140
|
-
val colors = intArrayOf(rippleColor)
|
|
141
|
-
ColorStateList(states, colors)
|
|
142
|
-
} else {
|
|
143
|
-
// if rippleColor is null, reapply the default color
|
|
144
|
-
context.theme.resolveAttribute(android.R.attr.colorControlHighlight, resolveOutValue, true)
|
|
145
|
-
val colors = intArrayOf(resolveOutValue.data)
|
|
146
|
-
ColorStateList(states, colors)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
selectable.setColor(colorStateList)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
val rippleRadius = rippleRadius
|
|
153
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && rippleRadius != null && selectable is RippleDrawable) {
|
|
154
|
-
selectable.radius = PixelUtil.toPixelFromDIP(rippleRadius.toFloat()).toInt()
|
|
155
|
-
}
|
|
156
|
-
return selectable
|
|
157
|
-
}
|
|
158
|
-
|
|
159
141
|
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
|
160
142
|
if (super.onInterceptTouchEvent(ev)) {
|
|
161
143
|
return true
|
|
@@ -210,49 +192,74 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
210
192
|
// reset foreground
|
|
211
193
|
foreground = null
|
|
212
194
|
}
|
|
195
|
+
|
|
196
|
+
val selectable = createSelectableDrawable()
|
|
197
|
+
|
|
198
|
+
if (borderRadius != 0f) {
|
|
199
|
+
// Radius-connected lines below ought to be considered
|
|
200
|
+
// as a temporary solution. It do not allow to set
|
|
201
|
+
// different radius on each corner. However, I suppose it's fairly
|
|
202
|
+
// fine for button-related use cases.
|
|
203
|
+
// Therefore it might be used as long as:
|
|
204
|
+
// 1. ReactViewManager is not a generic class with a possibility to handle another ViewGroup
|
|
205
|
+
// 2. There's no way to force native behavior of ReactViewGroup's superclass's onTouchEvent
|
|
206
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && selectable is RippleDrawable) {
|
|
207
|
+
val mask = PaintDrawable(Color.WHITE)
|
|
208
|
+
mask.setCornerRadius(borderRadius)
|
|
209
|
+
selectable.setDrawableByLayerId(android.R.id.mask, mask)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
213
|
if (useDrawableOnForeground && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
214
|
-
foreground =
|
|
214
|
+
foreground = selectable
|
|
215
215
|
if (_backgroundColor != Color.TRANSPARENT) {
|
|
216
216
|
setBackgroundColor(_backgroundColor)
|
|
217
217
|
}
|
|
218
218
|
} else if (_backgroundColor == Color.TRANSPARENT && rippleColor == null) {
|
|
219
|
-
background =
|
|
219
|
+
background = selectable
|
|
220
220
|
} else {
|
|
221
221
|
val colorDrawable = PaintDrawable(_backgroundColor)
|
|
222
|
-
|
|
222
|
+
|
|
223
223
|
if (borderRadius != 0f) {
|
|
224
|
-
// Radius-connected lines below ought to be considered
|
|
225
|
-
// as a temporary solution. It do not allow to set
|
|
226
|
-
// different radius on each corner. However, I suppose it's fairly
|
|
227
|
-
// fine for button-related use cases.
|
|
228
|
-
// Therefore it might be used as long as:
|
|
229
|
-
// 1. ReactViewManager is not a generic class with a possibility to handle another ViewGroup
|
|
230
|
-
// 2. There's no way to force native behavior of ReactViewGroup's superclass's onTouchEvent
|
|
231
224
|
colorDrawable.setCornerRadius(borderRadius)
|
|
232
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
|
|
233
|
-
&& selectable is RippleDrawable) {
|
|
234
|
-
val mask = PaintDrawable(Color.WHITE)
|
|
235
|
-
mask.setCornerRadius(borderRadius)
|
|
236
|
-
selectable.setDrawableByLayerId(android.R.id.mask, mask)
|
|
237
|
-
}
|
|
238
225
|
}
|
|
239
|
-
|
|
226
|
+
|
|
240
227
|
val layerDrawable = LayerDrawable(arrayOf(colorDrawable, selectable))
|
|
241
228
|
background = layerDrawable
|
|
242
229
|
}
|
|
243
230
|
}
|
|
244
231
|
|
|
245
232
|
private fun createSelectableDrawable(): Drawable {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
context.theme.resolveAttribute(attrID, resolveOutValue, true)
|
|
250
|
-
return if (version >= 21) {
|
|
251
|
-
resources.getDrawable(resolveOutValue.resourceId, context.theme)
|
|
252
|
-
} else {
|
|
233
|
+
// TODO: remove once support for RN 0.63 is dropped, since 0.64 minSdkVersion is 21
|
|
234
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
|
235
|
+
context.theme.resolveAttribute(android.R.attr.selectableItemBackground, resolveOutValue, true)
|
|
253
236
|
@Suppress("Deprecation")
|
|
254
|
-
resources.getDrawable(resolveOutValue.resourceId)
|
|
237
|
+
return resources.getDrawable(resolveOutValue.resourceId)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
val states = arrayOf(intArrayOf(android.R.attr.state_enabled))
|
|
241
|
+
val rippleRadius = rippleRadius
|
|
242
|
+
val colorStateList = if (rippleColor != null) {
|
|
243
|
+
val colors = intArrayOf(rippleColor!!)
|
|
244
|
+
ColorStateList(states, colors)
|
|
245
|
+
} else {
|
|
246
|
+
// if rippleColor is null, reapply the default color
|
|
247
|
+
context.theme.resolveAttribute(android.R.attr.colorControlHighlight, resolveOutValue, true)
|
|
248
|
+
val colors = intArrayOf(resolveOutValue.data)
|
|
249
|
+
ColorStateList(states, colors)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
val drawable = RippleDrawable(
|
|
253
|
+
colorStateList,
|
|
254
|
+
null,
|
|
255
|
+
if (useBorderlessDrawable) null else ShapeDrawable(RectShape())
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && rippleRadius != null) {
|
|
259
|
+
drawable.radius = PixelUtil.toPixelFromDIP(rippleRadius.toFloat()).toInt()
|
|
255
260
|
}
|
|
261
|
+
|
|
262
|
+
return drawable
|
|
256
263
|
}
|
|
257
264
|
|
|
258
265
|
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
|
@@ -265,7 +272,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
265
272
|
}
|
|
266
273
|
}
|
|
267
274
|
|
|
268
|
-
override fun
|
|
275
|
+
override fun canBegin(): Boolean {
|
|
269
276
|
val isResponder = tryGrabbingResponder()
|
|
270
277
|
if (isResponder) {
|
|
271
278
|
isTouched = true
|
|
@@ -273,11 +280,6 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
273
280
|
return isResponder
|
|
274
281
|
}
|
|
275
282
|
|
|
276
|
-
override fun afterGestureEnd() {
|
|
277
|
-
tryFreeingResponder()
|
|
278
|
-
isTouched = false
|
|
279
|
-
}
|
|
280
|
-
|
|
281
283
|
private fun tryGrabbingResponder(): Boolean {
|
|
282
284
|
if (isChildTouched()) {
|
|
283
285
|
return false
|
|
@@ -294,24 +296,30 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
294
296
|
}
|
|
295
297
|
}
|
|
296
298
|
|
|
297
|
-
private fun tryFreeingResponder() {
|
|
298
|
-
if (responder === this) {
|
|
299
|
-
responder = null
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
299
|
private fun isChildTouched(children: Sequence<View> = this.children): Boolean {
|
|
304
300
|
for (child in children) {
|
|
305
301
|
if (child is ButtonViewGroup && (child.isTouched || child.isPressed)) {
|
|
306
302
|
return true
|
|
307
303
|
} else if (child is ViewGroup) {
|
|
308
|
-
|
|
304
|
+
if (isChildTouched(child.children)) {
|
|
305
|
+
return true
|
|
306
|
+
}
|
|
309
307
|
}
|
|
310
308
|
}
|
|
311
309
|
|
|
312
310
|
return false
|
|
313
311
|
}
|
|
314
312
|
|
|
313
|
+
override fun performClick(): Boolean {
|
|
314
|
+
// don't preform click when a child button is pressed (mainly to prevent sound effect of
|
|
315
|
+
// a parent button from playing)
|
|
316
|
+
return if (!isChildTouched()) {
|
|
317
|
+
super.performClick()
|
|
318
|
+
} else {
|
|
319
|
+
false
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
315
323
|
override fun setPressed(pressed: Boolean) {
|
|
316
324
|
// there is a possibility of this method being called before NativeViewGestureHandler has
|
|
317
325
|
// opportunity to call canStart, in that case we need to grab responder in case the gesture
|
|
@@ -329,12 +337,13 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
329
337
|
if (!pressed || responder === this || canBePressedAlongsideOther) {
|
|
330
338
|
// we set pressed state only for current responder or any non-exclusive button when responder
|
|
331
339
|
// is null or non-exclusive, assuming it doesn't have pressed children
|
|
332
|
-
super.setPressed(pressed)
|
|
333
340
|
isTouched = pressed
|
|
341
|
+
super.setPressed(pressed)
|
|
334
342
|
}
|
|
335
343
|
if (!pressed && responder === this) {
|
|
336
344
|
// if the responder is no longer pressed we release button responder
|
|
337
345
|
responder = null
|
|
346
|
+
isTouched = false
|
|
338
347
|
}
|
|
339
348
|
}
|
|
340
349
|
|
|
@@ -344,28 +353,9 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
|
|
|
344
353
|
}
|
|
345
354
|
|
|
346
355
|
companion object {
|
|
347
|
-
const val SELECTABLE_ITEM_BACKGROUND = "selectableItemBackground"
|
|
348
|
-
const val SELECTABLE_ITEM_BACKGROUND_BORDERLESS = "selectableItemBackgroundBorderless"
|
|
349
|
-
|
|
350
356
|
var resolveOutValue = TypedValue()
|
|
351
357
|
var responder: ButtonViewGroup? = null
|
|
352
358
|
var dummyClickListener = OnClickListener { }
|
|
353
|
-
|
|
354
|
-
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
355
|
-
private fun getAttrId(context: Context, attr: String): Int {
|
|
356
|
-
SoftAssertions.assertNotNull(attr)
|
|
357
|
-
return when (attr) {
|
|
358
|
-
SELECTABLE_ITEM_BACKGROUND -> {
|
|
359
|
-
android.R.attr.selectableItemBackground
|
|
360
|
-
}
|
|
361
|
-
SELECTABLE_ITEM_BACKGROUND_BORDERLESS -> {
|
|
362
|
-
android.R.attr.selectableItemBackgroundBorderless
|
|
363
|
-
}
|
|
364
|
-
else -> {
|
|
365
|
-
context.resources.getIdentifier(attr, "attr", "android")
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
359
|
}
|
|
370
360
|
}
|
|
371
361
|
|
|
@@ -224,6 +224,9 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?)
|
|
|
224
224
|
if (config.hasKey(KEY_PAN_AVG_TOUCHES)) {
|
|
225
225
|
handler.setAverageTouches(config.getBoolean(KEY_PAN_AVG_TOUCHES))
|
|
226
226
|
}
|
|
227
|
+
if (config.hasKey(KEY_PAN_ACTIVATE_AFTER_LONG_PRESS)) {
|
|
228
|
+
handler.setActivateAfterLongPress(config.getInt(KEY_PAN_ACTIVATE_AFTER_LONG_PRESS).toLong())
|
|
229
|
+
}
|
|
227
230
|
}
|
|
228
231
|
|
|
229
232
|
override fun extractEventData(handler: PanGestureHandler, eventData: WritableMap) {
|
|
@@ -656,6 +659,7 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?)
|
|
|
656
659
|
private const val KEY_PAN_MIN_POINTERS = "minPointers"
|
|
657
660
|
private const val KEY_PAN_MAX_POINTERS = "maxPointers"
|
|
658
661
|
private const val KEY_PAN_AVG_TOUCHES = "avgTouches"
|
|
662
|
+
private const val KEY_PAN_ACTIVATE_AFTER_LONG_PRESS = "activateAfterLongPress"
|
|
659
663
|
private const val KEY_NUMBER_OF_POINTERS = "numberOfPointers"
|
|
660
664
|
private const val KEY_DIRECTION = "direction"
|
|
661
665
|
|
|
@@ -28,7 +28,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni
|
|
|
28
28
|
LOCAL_SHARED_LIBRARIES := libjsi \
|
|
29
29
|
libfbjni \
|
|
30
30
|
libglog \
|
|
31
|
-
|
|
31
|
+
libfolly_runtime \
|
|
32
32
|
libyoga \
|
|
33
33
|
libreact_nativemodule_core \
|
|
34
34
|
libturbomodulejsijni \
|
|
@@ -36,7 +36,6 @@ LOCAL_SHARED_LIBRARIES := libjsi \
|
|
|
36
36
|
libreact_render_core \
|
|
37
37
|
libreact_render_graphics \
|
|
38
38
|
libfabricjni \
|
|
39
|
-
libfolly_futures \
|
|
40
39
|
libreact_debug \
|
|
41
40
|
libreact_render_componentregistry \
|
|
42
41
|
libreact_render_debug \
|