react-native-gesture-handler 2.12.1 → 2.13.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.
Files changed (195) hide show
  1. package/RNGestureHandler.podspec +0 -15
  2. package/android/build.gradle +5 -76
  3. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +16 -1
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +43 -18
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/HoverGestureHandler.kt +120 -0
  6. package/android/src/main/java/com/swmansion/gesturehandler/core/NativeViewGestureHandler.kt +7 -0
  7. package/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt +5 -0
  8. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +43 -16
  9. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +21 -0
  10. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt +9 -1
  11. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootView.kt +10 -0
  12. package/ios/Handlers/RNHoverHandler.h +12 -0
  13. package/ios/Handlers/RNHoverHandler.m +152 -0
  14. package/ios/RNGestureHandlerButtonComponentView.mm +2 -0
  15. package/ios/RNGestureHandlerEvents.h +1 -0
  16. package/ios/RNGestureHandlerEvents.m +10 -0
  17. package/ios/RNGestureHandlerManager.mm +2 -0
  18. package/ios/RNManualActivationRecognizer.m +2 -1
  19. package/ios/RNRootViewGestureRecognizer.m +7 -2
  20. package/lib/commonjs/RNGestureHandlerModule.macos.js +3 -1
  21. package/lib/commonjs/RNGestureHandlerModule.macos.js.map +1 -1
  22. package/lib/commonjs/RNGestureHandlerModule.web.js +7 -2
  23. package/lib/commonjs/RNGestureHandlerModule.web.js.map +1 -1
  24. package/lib/commonjs/RNGestureHandlerModule.windows.js +3 -1
  25. package/lib/commonjs/RNGestureHandlerModule.windows.js.map +1 -1
  26. package/lib/commonjs/components/DrawerLayout.js +1 -0
  27. package/lib/commonjs/components/DrawerLayout.js.map +1 -1
  28. package/lib/commonjs/components/touchables/GenericTouchable.js +1 -2
  29. package/lib/commonjs/components/touchables/GenericTouchable.js.map +1 -1
  30. package/lib/commonjs/components/touchables/TouchableHighlight.js.map +1 -1
  31. package/lib/commonjs/components/touchables/TouchableNativeFeedback.android.js.map +1 -1
  32. package/lib/commonjs/components/touchables/TouchableOpacity.js.map +1 -1
  33. package/lib/commonjs/components/touchables/TouchableWithoutFeedback.js.map +1 -1
  34. package/lib/commonjs/components/touchables/index.js.map +1 -1
  35. package/lib/commonjs/handlers/createHandler.js +3 -3
  36. package/lib/commonjs/handlers/createHandler.js.map +1 -1
  37. package/lib/commonjs/handlers/gestureHandlerCommon.js +1 -1
  38. package/lib/commonjs/handlers/gestureHandlerCommon.js.map +1 -1
  39. package/lib/commonjs/handlers/gestures/GestureDetector.js +3 -1
  40. package/lib/commonjs/handlers/gestures/GestureDetector.js.map +1 -1
  41. package/lib/commonjs/handlers/gestures/gesture.js +5 -0
  42. package/lib/commonjs/handlers/gestures/gesture.js.map +1 -1
  43. package/lib/commonjs/handlers/gestures/gestureObjects.js +5 -0
  44. package/lib/commonjs/handlers/gestures/gestureObjects.js.map +1 -1
  45. package/lib/commonjs/handlers/gestures/hoverGesture.js +74 -0
  46. package/lib/commonjs/handlers/gestures/hoverGesture.js.map +1 -0
  47. package/lib/commonjs/index.js +8 -0
  48. package/lib/commonjs/index.js.map +1 -1
  49. package/lib/commonjs/web/handlers/FlingGestureHandler.js +0 -10
  50. package/lib/commonjs/web/handlers/FlingGestureHandler.js.map +1 -1
  51. package/lib/commonjs/web/handlers/GestureHandler.js +61 -88
  52. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  53. package/lib/commonjs/web/handlers/HoverGestureHandler.js +62 -0
  54. package/lib/commonjs/web/handlers/HoverGestureHandler.js.map +1 -0
  55. package/lib/commonjs/web/handlers/LongPressGestureHandler.js +6 -7
  56. package/lib/commonjs/web/handlers/LongPressGestureHandler.js.map +1 -1
  57. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js +12 -4
  58. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js.map +1 -1
  59. package/lib/commonjs/web/handlers/PanGestureHandler.js +2 -7
  60. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  61. package/lib/commonjs/web/handlers/TapGestureHandler.js +0 -10
  62. package/lib/commonjs/web/handlers/TapGestureHandler.js.map +1 -1
  63. package/lib/commonjs/web/interfaces.js +1 -1
  64. package/lib/commonjs/web/interfaces.js.map +1 -1
  65. package/lib/commonjs/web/tools/EventManager.js +18 -4
  66. package/lib/commonjs/web/tools/EventManager.js.map +1 -1
  67. package/lib/commonjs/web/tools/GestureHandlerDelegate.js +6 -0
  68. package/lib/commonjs/web/tools/GestureHandlerDelegate.js.map +1 -0
  69. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js +5 -7
  70. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  71. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +118 -0
  72. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -0
  73. package/lib/commonjs/web/tools/PointerEventManager.js +38 -5
  74. package/lib/commonjs/web/tools/PointerEventManager.js.map +1 -1
  75. package/lib/commonjs/web/tools/TouchEventManager.js +2 -2
  76. package/lib/commonjs/web/tools/TouchEventManager.js.map +1 -1
  77. package/lib/module/RNGestureHandlerModule.macos.js +2 -1
  78. package/lib/module/RNGestureHandlerModule.macos.js.map +1 -1
  79. package/lib/module/RNGestureHandlerModule.web.js +6 -3
  80. package/lib/module/RNGestureHandlerModule.web.js.map +1 -1
  81. package/lib/module/RNGestureHandlerModule.windows.js +2 -1
  82. package/lib/module/RNGestureHandlerModule.windows.js.map +1 -1
  83. package/lib/module/components/DrawerLayout.js +1 -0
  84. package/lib/module/components/DrawerLayout.js.map +1 -1
  85. package/lib/module/components/touchables/GenericTouchable.js +1 -2
  86. package/lib/module/components/touchables/GenericTouchable.js.map +1 -1
  87. package/lib/module/components/touchables/TouchableHighlight.js.map +1 -1
  88. package/lib/module/components/touchables/TouchableNativeFeedback.android.js.map +1 -1
  89. package/lib/module/components/touchables/TouchableOpacity.js.map +1 -1
  90. package/lib/module/components/touchables/TouchableWithoutFeedback.js.map +1 -1
  91. package/lib/module/components/touchables/index.js.map +1 -1
  92. package/lib/module/handlers/createHandler.js +3 -3
  93. package/lib/module/handlers/createHandler.js.map +1 -1
  94. package/lib/module/handlers/gestureHandlerCommon.js +1 -1
  95. package/lib/module/handlers/gestureHandlerCommon.js.map +1 -1
  96. package/lib/module/handlers/gestures/GestureDetector.js +2 -1
  97. package/lib/module/handlers/gestures/GestureDetector.js.map +1 -1
  98. package/lib/module/handlers/gestures/gesture.js +5 -0
  99. package/lib/module/handlers/gestures/gesture.js.map +1 -1
  100. package/lib/module/handlers/gestures/gestureObjects.js +4 -0
  101. package/lib/module/handlers/gestures/gestureObjects.js.map +1 -1
  102. package/lib/module/handlers/gestures/hoverGesture.js +62 -0
  103. package/lib/module/handlers/gestures/hoverGesture.js.map +1 -0
  104. package/lib/module/index.js +1 -0
  105. package/lib/module/index.js.map +1 -1
  106. package/lib/module/web/handlers/FlingGestureHandler.js +0 -10
  107. package/lib/module/web/handlers/FlingGestureHandler.js.map +1 -1
  108. package/lib/module/web/handlers/GestureHandler.js +61 -84
  109. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  110. package/lib/module/web/handlers/HoverGestureHandler.js +47 -0
  111. package/lib/module/web/handlers/HoverGestureHandler.js.map +1 -0
  112. package/lib/module/web/handlers/LongPressGestureHandler.js +5 -7
  113. package/lib/module/web/handlers/LongPressGestureHandler.js.map +1 -1
  114. package/lib/module/web/handlers/NativeViewGestureHandler.js +11 -4
  115. package/lib/module/web/handlers/NativeViewGestureHandler.js.map +1 -1
  116. package/lib/module/web/handlers/PanGestureHandler.js +2 -7
  117. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  118. package/lib/module/web/handlers/TapGestureHandler.js +0 -10
  119. package/lib/module/web/handlers/TapGestureHandler.js.map +1 -1
  120. package/lib/module/web/interfaces.js +1 -1
  121. package/lib/module/web/interfaces.js.map +1 -1
  122. package/lib/module/web/tools/EventManager.js +18 -4
  123. package/lib/module/web/tools/EventManager.js.map +1 -1
  124. package/lib/module/web/tools/GestureHandlerDelegate.js +2 -0
  125. package/lib/module/web/tools/GestureHandlerDelegate.js.map +1 -0
  126. package/lib/module/web/tools/GestureHandlerOrchestrator.js +5 -6
  127. package/lib/module/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  128. package/lib/module/web/tools/GestureHandlerWebDelegate.js +102 -0
  129. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -0
  130. package/lib/module/web/tools/PointerEventManager.js +39 -6
  131. package/lib/module/web/tools/PointerEventManager.js.map +1 -1
  132. package/lib/module/web/tools/TouchEventManager.js +2 -2
  133. package/lib/module/web/tools/TouchEventManager.js.map +1 -1
  134. package/lib/typescript/RNGestureHandlerModule.macos.d.ts +1 -1
  135. package/lib/typescript/RNGestureHandlerModule.web.d.ts +3 -1
  136. package/lib/typescript/RNGestureHandlerModule.windows.d.ts +1 -1
  137. package/lib/typescript/components/DrawerLayout.d.ts +7 -1
  138. package/lib/typescript/components/touchables/GenericTouchable.d.ts +3 -2
  139. package/lib/typescript/components/touchables/TouchableHighlight.d.ts +4 -3
  140. package/lib/typescript/components/touchables/TouchableNativeFeedback.android.d.ts +3 -2
  141. package/lib/typescript/components/touchables/TouchableOpacity.d.ts +4 -5
  142. package/lib/typescript/components/touchables/TouchableWithoutFeedback.d.ts +1 -0
  143. package/lib/typescript/components/touchables/index.d.ts +3 -0
  144. package/lib/typescript/handlers/NativeViewGestureHandler.d.ts +1 -1
  145. package/lib/typescript/handlers/gestureHandlerCommon.d.ts +3 -1
  146. package/lib/typescript/handlers/gestures/gesture.d.ts +2 -1
  147. package/lib/typescript/handlers/gestures/gestureObjects.d.ts +2 -0
  148. package/lib/typescript/handlers/gestures/hoverGesture.d.ts +32 -0
  149. package/lib/typescript/index.d.ts +4 -0
  150. package/lib/typescript/web/handlers/FlingGestureHandler.d.ts +0 -6
  151. package/lib/typescript/web/handlers/GestureHandler.d.ts +12 -17
  152. package/lib/typescript/web/handlers/HoverGestureHandler.d.ts +10 -0
  153. package/lib/typescript/web/handlers/LongPressGestureHandler.d.ts +0 -4
  154. package/lib/typescript/web/handlers/NativeViewGestureHandler.d.ts +1 -1
  155. package/lib/typescript/web/handlers/PanGestureHandler.d.ts +0 -4
  156. package/lib/typescript/web/handlers/TapGestureHandler.d.ts +0 -6
  157. package/lib/typescript/web/interfaces.d.ts +4 -3
  158. package/lib/typescript/web/tools/EventManager.d.ts +9 -5
  159. package/lib/typescript/web/tools/GestureHandlerDelegate.d.ts +22 -0
  160. package/lib/typescript/web/tools/GestureHandlerWebDelegate.d.ts +21 -0
  161. package/lib/typescript/web/tools/NodeManager.d.ts +2 -2
  162. package/lib/typescript/web/tools/PointerEventManager.d.ts +1 -1
  163. package/lib/typescript/web/tools/TouchEventManager.d.ts +1 -1
  164. package/package.json +2 -2
  165. package/src/RNGestureHandlerModule.macos.ts +5 -1
  166. package/src/RNGestureHandlerModule.web.ts +7 -1
  167. package/src/RNGestureHandlerModule.windows.ts +5 -1
  168. package/src/components/DrawerLayout.tsx +9 -0
  169. package/src/components/touchables/GenericTouchable.tsx +4 -4
  170. package/src/components/touchables/TouchableHighlight.tsx +6 -3
  171. package/src/components/touchables/TouchableNativeFeedback.android.tsx +5 -4
  172. package/src/components/touchables/TouchableOpacity.tsx +6 -7
  173. package/src/components/touchables/TouchableWithoutFeedback.tsx +3 -1
  174. package/src/components/touchables/index.ts +3 -0
  175. package/src/handlers/createHandler.tsx +4 -2
  176. package/src/handlers/gestureHandlerCommon.ts +39 -0
  177. package/src/handlers/gestures/GestureDetector.tsx +2 -0
  178. package/src/handlers/gestures/gesture.ts +6 -0
  179. package/src/handlers/gestures/gestureObjects.ts +5 -0
  180. package/src/handlers/gestures/hoverGesture.ts +83 -0
  181. package/src/index.ts +8 -0
  182. package/src/web/handlers/FlingGestureHandler.ts +0 -11
  183. package/src/web/handlers/GestureHandler.ts +68 -88
  184. package/src/web/handlers/HoverGestureHandler.ts +43 -0
  185. package/src/web/handlers/LongPressGestureHandler.ts +5 -7
  186. package/src/web/handlers/NativeViewGestureHandler.ts +11 -4
  187. package/src/web/handlers/PanGestureHandler.ts +1 -6
  188. package/src/web/handlers/TapGestureHandler.ts +0 -11
  189. package/src/web/interfaces.ts +4 -2
  190. package/src/web/tools/EventManager.ts +15 -7
  191. package/src/web/tools/GestureHandlerDelegate.ts +23 -0
  192. package/src/web/tools/GestureHandlerOrchestrator.ts +7 -6
  193. package/src/web/tools/GestureHandlerWebDelegate.ts +115 -0
  194. package/src/web/tools/PointerEventManager.ts +46 -16
  195. package/src/web/tools/TouchEventManager.ts +3 -3
@@ -2,21 +2,6 @@ require "json"
2
2
 
3
3
  fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
4
4
 
5
- isUserApp = File.exist?(File.join(__dir__, "..", "..", "node_modules", "react-native", "package.json"))
6
- if isUserApp
7
- libInstances = %x[find ../../ -name "package.json" | grep "/react-native-gesture-handler/package.json" | grep -v "/.yarn/"]
8
- libInstancesArray = libInstances.split("\n")
9
- if libInstancesArray.length() > 1
10
- parsedLocation = ''
11
- for location in libInstancesArray
12
- location['../../'] = '- '
13
- location['/package.json'] = ''
14
- parsedLocation += location + "\n"
15
- end
16
- raise "[Gesture Handler] Multiple versions of Gesture Handler were detected. Only one instance of react-native-gesture-handler can be installed in a project. You need to resolve the conflict manually. Check out the documentation: https://docs.swmansion.com/react-native-gesture-handler/docs/troubleshooting#multiple-versions-of-reanimated-were-detected \n\nConflict between: \n" + parsedLocation
17
- end
18
- end
19
-
20
5
  Pod::Spec.new do |s|
21
6
  # NPM package specification
22
7
  package = JSON.parse(File.read(File.join(File.dirname(__FILE__), "package.json")))
@@ -68,14 +68,6 @@ if (project == rootProject) {
68
68
  apply from: "spotless.gradle"
69
69
  }
70
70
 
71
- def shouldAssertNoMultipleInstances() {
72
- if (rootProject.hasProperty("disableMultipleInstancesCheck")) {
73
- return rootProject.property("disableMultipleInstancesCheck") != "true"
74
- } else {
75
- return true
76
- }
77
- }
78
-
79
71
  // Check whether Reanimated 2.3 or higher is installed alongside Gesture Handler
80
72
  def shouldUseCommonInterfaceFromReanimated() {
81
73
  def reanimated = rootProject.subprojects.find { it.name == 'react-native-reanimated' }
@@ -103,71 +95,12 @@ file("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { react
103
95
  def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME")
104
96
  def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
105
97
 
106
-
107
- abstract class NoMultipleInstancesAssertionTask extends DefaultTask {
108
- @Inject abstract ObjectFactory getObjectFactory()
109
-
110
- @Input abstract Property<File> getProjectDirFile()
111
- @Input abstract Property<File> getRootDirFile()
112
- @Input abstract Property<Boolean> getShouldCheck()
113
-
114
- def findGestureHandlerInstancesForPath(String path) {
115
- return objectFactory.fileTree().from(path)
116
- .include("**/react-native-gesture-handler/package.json")
117
- .exclude("**/.yarn/**")
118
- .exclude({ Files.isSymbolicLink(it.getFile().toPath()) })
119
- .findAll()
120
- }
121
-
122
- @TaskAction
123
- def check() {
124
- if (shouldCheck.get()) {
125
- // Assert there are no multiple installations of Gesture Handler
126
- Set<File> files
127
-
128
- if (projectDirFile.get().parent.contains(rootDirFile.get().parent)) {
129
- // standard app
130
- files = findGestureHandlerInstancesForPath(rootDirFile.get().parent + "/node_modules")
131
- } else {
132
- // monorepo
133
- files = findGestureHandlerInstancesForPath(rootDirFile.get().parent + "/node_modules")
134
- files.addAll(
135
- findGestureHandlerInstancesForPath(projectDirFile.get().parentFile.parent)
136
- )
137
- }
138
-
139
- if (files.size() > 1) {
140
- String parsedLocation = files.stream().map({
141
- File file -> "- " + file.toString().replace("/package.json", "")
142
- }).collect().join("\n")
143
- String exceptionMessage = "\n[react-native-gesture-handler] Multiple versions of Gesture Handler " +
144
- "were detected. Only one instance of react-native-gesture-handler can be installed in a " +
145
- "project. You need to resolve the conflict manually. Check out the documentation: " +
146
- "https://docs.swmansion.com/react-native-gesture-handler/docs/troubleshooting" +
147
- "#multiple-instances-of-gesture-handler-were-detected \n\nConflict between: \n" +
148
- parsedLocation + "\n"
149
- throw new GradleException(exceptionMessage)
150
- }
151
- }
152
- }
153
- }
154
-
155
- tasks.register('assertNoMultipleInstances', NoMultipleInstancesAssertionTask) {
156
- shouldCheck = shouldAssertNoMultipleInstances()
157
- rootDirFile = rootDir
158
- projectDirFile = projectDir
159
- }
160
-
161
- tasks.preBuild {
162
- dependsOn assertNoMultipleInstances
163
- }
164
-
165
98
  repositories {
166
99
  mavenCentral()
167
100
  }
168
101
 
169
102
  android {
170
- compileSdkVersion safeExtGet("compileSdkVersion", 28)
103
+ compileSdkVersion safeExtGet("compileSdkVersion", 33)
171
104
  def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
172
105
  if (agpVersion.tokenize('.')[0].toInteger() >= 7) {
173
106
  namespace "com.swmansion.gesturehandler"
@@ -189,8 +122,8 @@ android {
189
122
  }
190
123
 
191
124
  defaultConfig {
192
- minSdkVersion safeExtGet('minSdkVersion', 16)
193
- targetSdkVersion safeExtGet('targetSdkVersion', 28)
125
+ minSdkVersion safeExtGet('minSdkVersion', 21)
126
+ targetSdkVersion safeExtGet('targetSdkVersion', 33)
194
127
  versionCode 1
195
128
  versionName "1.0"
196
129
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
@@ -256,12 +189,8 @@ android {
256
189
  def kotlin_version = safeExtGet('kotlinVersion', project.properties['RNGH_kotlinVersion'])
257
190
 
258
191
  dependencies {
259
- //noinspection GradleDynamicVersion
260
- if (REACT_NATIVE_MINOR_VERSION >= 71) {
261
- implementation "com.facebook.react:react-android" // version substituted by RNGP
262
- } else {
263
- implementation 'com.facebook.react:react-native:+' // from node_modules
264
- }
192
+ implementation 'com.facebook.react:react-native:+' // from node_modules
193
+
265
194
 
266
195
  if (shouldUseCommonInterfaceFromReanimated()) {
267
196
  // Include Reanimated as dependency to load the common interface
@@ -371,7 +371,14 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
371
371
  lastAbsolutePositionY = GestureUtils.getLastPointerY(adaptedTransformedEvent, true)
372
372
  lastEventOffsetX = adaptedTransformedEvent.rawX - adaptedTransformedEvent.x
373
373
  lastEventOffsetY = adaptedTransformedEvent.rawY - adaptedTransformedEvent.y
374
- onHandle(adaptedTransformedEvent, adaptedSourceEvent)
374
+ if (sourceEvent.action == MotionEvent.ACTION_HOVER_ENTER ||
375
+ sourceEvent.action == MotionEvent.ACTION_HOVER_MOVE ||
376
+ sourceEvent.action == MotionEvent.ACTION_HOVER_EXIT
377
+ ) {
378
+ onHandleHover(adaptedTransformedEvent, adaptedSourceEvent)
379
+ } else {
380
+ onHandle(adaptedTransformedEvent, adaptedSourceEvent)
381
+ }
375
382
  if (adaptedTransformedEvent != transformedEvent) {
376
383
  adaptedTransformedEvent.recycle()
377
384
  }
@@ -675,6 +682,8 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
675
682
  moveToState(STATE_FAILED)
676
683
  }
677
684
 
685
+ protected open fun onHandleHover(event: MotionEvent, sourceEvent: MotionEvent) {}
686
+
678
687
  protected open fun onStateChange(newState: Int, previousState: Int) {}
679
688
  protected open fun onReset() {}
680
689
  protected open fun onCancel() {}
@@ -706,6 +715,12 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
706
715
  onReset()
707
716
  }
708
717
 
718
+ fun withMarkedAsInBounds(closure: () -> Unit) {
719
+ isWithinBounds = true
720
+ closure()
721
+ isWithinBounds = false
722
+ }
723
+
709
724
  fun setOnTouchEventListener(listener: OnTouchEventListener?): GestureHandler<*> {
710
725
  onTouchEventListener = listener
711
726
  return this
@@ -37,7 +37,7 @@ class GestureHandlerOrchestrator(
37
37
  fun onTouchEvent(event: MotionEvent): Boolean {
38
38
  isHandlingTouch = true
39
39
  val action = event.actionMasked
40
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {
40
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN || action == MotionEvent.ACTION_HOVER_MOVE) {
41
41
  extractGestureHandlers(event)
42
42
  } else if (action == MotionEvent.ACTION_CANCEL) {
43
43
  cancelAll()
@@ -295,7 +295,7 @@ class GestureHandlerOrchestrator(
295
295
 
296
296
  // if event was of type UP or POINTER_UP we request handler to stop tracking now that
297
297
  // the event has been dispatched
298
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
298
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_HOVER_EXIT) {
299
299
  val pointerId = event.getPointerId(event.actionIndex)
300
300
  handler.stopTrackingPointer(pointerId)
301
301
  }
@@ -325,6 +325,8 @@ class GestureHandlerOrchestrator(
325
325
  return parent === wrapperView
326
326
  }
327
327
 
328
+ fun isAnyHandlerActive() = gestureHandlers.any { it?.state == GestureHandler.STATE_ACTIVE }
329
+
328
330
  /**
329
331
  * Transforms an event in the coordinates of wrapperView into the coordinate space of the received view.
330
332
  *
@@ -464,16 +466,24 @@ class GestureHandlerOrchestrator(
464
466
  return found
465
467
  }
466
468
 
467
- private fun recordViewHandlersForPointer(view: View, coords: FloatArray, pointerId: Int): Boolean {
469
+ private fun recordViewHandlersForPointer(view: View, coords: FloatArray, pointerId: Int, event: MotionEvent): Boolean {
468
470
  var found = false
469
471
  handlerRegistry.getHandlersForView(view)?.let {
470
472
  synchronized(it) {
471
473
  for (handler in it) {
472
- if (handler.isEnabled && handler.isWithinBounds(view, coords[0], coords[1])) {
473
- recordHandlerIfNotPresent(handler, view)
474
- handler.startTrackingPointer(pointerId)
475
- found = true
474
+ // skip disabled and out-of-bounds handlers
475
+ if (!handler.isEnabled || !handler.isWithinBounds(view, coords[0], coords[1])) {
476
+ continue
477
+ }
478
+
479
+ // we don't want to extract gestures other than hover when processing hover events
480
+ if (event.action in listOf(MotionEvent.ACTION_HOVER_EXIT, MotionEvent.ACTION_HOVER_ENTER, MotionEvent.ACTION_HOVER_MOVE) && handler !is HoverGestureHandler) {
481
+ continue
476
482
  }
483
+
484
+ recordHandlerIfNotPresent(handler, view)
485
+ handler.startTrackingPointer(pointerId)
486
+ found = true
477
487
  }
478
488
  }
479
489
  }
@@ -494,11 +504,11 @@ class GestureHandlerOrchestrator(
494
504
  val pointerId = event.getPointerId(actionIndex)
495
505
  tempCoords[0] = event.getX(actionIndex)
496
506
  tempCoords[1] = event.getY(actionIndex)
497
- traverseWithPointerEvents(wrapperView, tempCoords, pointerId)
498
- extractGestureHandlers(wrapperView, tempCoords, pointerId)
507
+ traverseWithPointerEvents(wrapperView, tempCoords, pointerId, event)
508
+ extractGestureHandlers(wrapperView, tempCoords, pointerId, event)
499
509
  }
500
510
 
501
- private fun extractGestureHandlers(viewGroup: ViewGroup, coords: FloatArray, pointerId: Int): Boolean {
511
+ private fun extractGestureHandlers(viewGroup: ViewGroup, coords: FloatArray, pointerId: Int, event: MotionEvent): Boolean {
502
512
  val childrenCount = viewGroup.childCount
503
513
  for (i in childrenCount - 1 downTo 0) {
504
514
  val child = viewConfigHelper.getChildInDrawingOrderAtIndex(viewGroup, i)
@@ -513,7 +523,7 @@ class GestureHandlerOrchestrator(
513
523
  if (!isClipping(child) || isTransformedTouchPointInView(coords[0], coords[1], child)) {
514
524
  // we only consider the view if touch is inside the view bounds or if the view's children
515
525
  // can render outside of the view bounds (overflow visible)
516
- found = traverseWithPointerEvents(child, coords, pointerId)
526
+ found = traverseWithPointerEvents(child, coords, pointerId, event)
517
527
  }
518
528
  coords[0] = restoreX
519
529
  coords[1] = restoreY
@@ -525,7 +535,7 @@ class GestureHandlerOrchestrator(
525
535
  return false
526
536
  }
527
537
 
528
- private fun traverseWithPointerEvents(view: View, coords: FloatArray, pointerId: Int): Boolean =
538
+ private fun traverseWithPointerEvents(view: View, coords: FloatArray, pointerId: Int, event: MotionEvent): Boolean =
529
539
  when (viewConfigHelper.getPointerEventsConfigForView(view)) {
530
540
  PointerEventsConfig.NONE -> {
531
541
  // This view and its children can't be the target
@@ -534,7 +544,7 @@ class GestureHandlerOrchestrator(
534
544
  PointerEventsConfig.BOX_ONLY -> {
535
545
  // This view is the target, its children don't matter
536
546
  (
537
- recordViewHandlersForPointer(view, coords, pointerId) ||
547
+ recordViewHandlersForPointer(view, coords, pointerId, event) ||
538
548
  shouldHandlerlessViewBecomeTouchTarget(view, coords)
539
549
  )
540
550
  }
@@ -542,10 +552,10 @@ class GestureHandlerOrchestrator(
542
552
  // This view can't be the target, but its children might
543
553
  when (view) {
544
554
  is ViewGroup -> {
545
- extractGestureHandlers(view, coords, pointerId).also { found ->
555
+ extractGestureHandlers(view, coords, pointerId, event).also { found ->
546
556
  // A child view is handling touch, also extract handlers attached to this view
547
557
  if (found) {
548
- recordViewHandlersForPointer(view, coords, pointerId)
558
+ recordViewHandlersForPointer(view, coords, pointerId, event)
549
559
  }
550
560
  }
551
561
  }
@@ -554,7 +564,7 @@ class GestureHandlerOrchestrator(
554
564
  // handlers attached to the text input, as it makes sense that gestures would work on a
555
565
  // non-editable TextInput.
556
566
  is EditText -> {
557
- recordViewHandlersForPointer(view, coords, pointerId)
567
+ recordViewHandlersForPointer(view, coords, pointerId, event)
558
568
  }
559
569
  else -> false
560
570
  }
@@ -562,11 +572,11 @@ class GestureHandlerOrchestrator(
562
572
  PointerEventsConfig.AUTO -> {
563
573
  // Either this view or one of its children is the target
564
574
  val found = if (view is ViewGroup) {
565
- extractGestureHandlers(view, coords, pointerId)
575
+ extractGestureHandlers(view, coords, pointerId, event)
566
576
  } else false
567
577
 
568
578
  (
569
- recordViewHandlersForPointer(view, coords, pointerId) ||
579
+ recordViewHandlersForPointer(view, coords, pointerId, event) ||
570
580
  found || shouldHandlerlessViewBecomeTouchTarget(view, coords)
571
581
  )
572
582
  }
@@ -580,6 +590,21 @@ class GestureHandlerOrchestrator(
580
590
  private fun isClipping(view: View) =
581
591
  view !is ViewGroup || viewConfigHelper.isViewClippingChildren(view)
582
592
 
593
+ fun activateNativeHandlersForView(view: View) {
594
+ handlerRegistry.getHandlersForView(view)?.forEach {
595
+ if (it !is NativeViewGestureHandler) {
596
+ return@forEach
597
+ }
598
+ this.recordHandlerIfNotPresent(it, view)
599
+
600
+ it.withMarkedAsInBounds {
601
+ it.begin()
602
+ it.activate()
603
+ it.end()
604
+ }
605
+ }
606
+ }
607
+
583
608
  companion object {
584
609
  // The limit doesn't necessarily need to exists, it was just simpler to implement it that way
585
610
  // it is also more allocation-wise efficient to have a fixed limit
@@ -0,0 +1,120 @@
1
+ package com.swmansion.gesturehandler.core
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import android.view.MotionEvent
6
+ import android.view.View
7
+ import android.view.ViewGroup
8
+ import com.swmansion.gesturehandler.react.RNViewConfigurationHelper
9
+
10
+ class HoverGestureHandler : GestureHandler<HoverGestureHandler>() {
11
+ private var handler: Handler? = null
12
+ private var finishRunnable = Runnable { finish() }
13
+
14
+ private infix fun isAncestorOf(other: GestureHandler<*>): Boolean {
15
+ var current: View? = other.view
16
+
17
+ while (current != null) {
18
+ if (current == this.view) {
19
+ return true
20
+ }
21
+
22
+ current = current.parent as? View
23
+ }
24
+
25
+ return false
26
+ }
27
+
28
+ private fun isViewDisplayedOverAnother(view: View, other: View, rootView: View = view.rootView): Boolean? {
29
+ // traverse the tree starting on the root view, to see which view will be drawn first
30
+ if (rootView == other) {
31
+ return true
32
+ }
33
+
34
+ if (rootView == view) {
35
+ return false
36
+ }
37
+
38
+ if (rootView is ViewGroup) {
39
+ for (i in 0 until rootView.childCount) {
40
+ val child = viewConfigHelper.getChildInDrawingOrderAtIndex(rootView, i)
41
+ return isViewDisplayedOverAnother(view, other, child) ?: continue
42
+ }
43
+ }
44
+
45
+ return null
46
+ }
47
+
48
+ override fun shouldBeCancelledBy(handler: GestureHandler<*>): Boolean {
49
+ if (handler is HoverGestureHandler && !(handler isAncestorOf this)) {
50
+ return isViewDisplayedOverAnother(handler.view!!, this.view!!)!!
51
+ }
52
+
53
+ return super.shouldBeCancelledBy(handler)
54
+ }
55
+
56
+ override fun shouldRequireToWaitForFailure(handler: GestureHandler<*>): Boolean {
57
+ if (handler is HoverGestureHandler) {
58
+ if (!(this isAncestorOf handler) && !(handler isAncestorOf this)) {
59
+ isViewDisplayedOverAnother(this.view!!, handler.view!!)?.let {
60
+ return it
61
+ }
62
+ }
63
+ }
64
+
65
+ return super.shouldRequireToWaitForFailure(handler)
66
+ }
67
+
68
+ override fun shouldRecognizeSimultaneously(handler: GestureHandler<*>): Boolean {
69
+ if (handler is HoverGestureHandler && (this isAncestorOf handler || handler isAncestorOf this)) {
70
+ return true
71
+ }
72
+
73
+ return super.shouldRecognizeSimultaneously(handler)
74
+ }
75
+
76
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
77
+ if (event.action == MotionEvent.ACTION_DOWN) {
78
+ handler?.removeCallbacksAndMessages(null)
79
+ handler = null
80
+ } else if (event.action == MotionEvent.ACTION_UP) {
81
+ if (!isWithinBounds) {
82
+ finish()
83
+ }
84
+ }
85
+ }
86
+
87
+ override fun onHandleHover(event: MotionEvent, sourceEvent: MotionEvent) {
88
+ when {
89
+ event.action == MotionEvent.ACTION_HOVER_EXIT -> {
90
+ if (handler == null) {
91
+ handler = Handler(Looper.getMainLooper())
92
+ }
93
+
94
+ handler!!.postDelayed(finishRunnable, 4)
95
+ }
96
+
97
+ !isWithinBounds -> {
98
+ finish()
99
+ }
100
+
101
+ this.state == STATE_UNDETERMINED &&
102
+ (event.action == MotionEvent.ACTION_HOVER_MOVE || event.action == MotionEvent.ACTION_HOVER_ENTER) -> {
103
+ begin()
104
+ activate()
105
+ }
106
+ }
107
+ }
108
+
109
+ private fun finish() {
110
+ when (this.state) {
111
+ STATE_UNDETERMINED -> cancel()
112
+ STATE_BEGAN -> fail()
113
+ STATE_ACTIVE -> end()
114
+ }
115
+ }
116
+
117
+ companion object {
118
+ private val viewConfigHelper = RNViewConfigurationHelper()
119
+ }
120
+ }
@@ -6,6 +6,7 @@ import android.view.View
6
6
  import android.view.ViewConfiguration
7
7
  import android.view.ViewGroup
8
8
  import android.widget.ScrollView
9
+ import com.facebook.react.views.scroll.ReactScrollView
9
10
  import com.facebook.react.views.swiperefresh.ReactSwipeRefreshLayout
10
11
  import com.facebook.react.views.textinput.ReactEditText
11
12
 
@@ -75,6 +76,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
75
76
  is NativeViewGestureHandlerHook -> this.hook = view
76
77
  is ReactEditText -> this.hook = EditTextHook(this, view)
77
78
  is ReactSwipeRefreshLayout -> this.hook = SwipeRefreshLayoutHook(this, view)
79
+ is ReactScrollView -> this.hook = ScrollViewHook()
78
80
  }
79
81
  }
80
82
 
@@ -120,6 +122,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
120
122
  action = MotionEvent.ACTION_CANCEL
121
123
  }
122
124
  view!!.onTouchEvent(event)
125
+ event.recycle()
123
126
  }
124
127
 
125
128
  override fun onReset() {
@@ -247,4 +250,8 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
247
250
  // oh well ¯\_(ツ)_/¯
248
251
  }
249
252
  }
253
+
254
+ private class ScrollViewHook : NativeViewGestureHandlerHook {
255
+ override fun shouldCancelRootViewGestureHandlerIfNecessary() = true
256
+ }
250
257
  }
@@ -1,5 +1,7 @@
1
1
  package com.swmansion.gesturehandler.react
2
2
 
3
+ import android.content.Context
4
+ import android.view.accessibility.AccessibilityManager
3
5
  import com.facebook.react.bridge.ReactContext
4
6
  import com.facebook.react.modules.core.DeviceEventManagerModule
5
7
  import com.facebook.react.uimanager.UIManagerModule
@@ -9,3 +11,6 @@ val ReactContext.deviceEventEmitter: DeviceEventManagerModule.RCTDeviceEventEmit
9
11
 
10
12
  val ReactContext.UIManager: UIManagerModule
11
13
  get() = this.getNativeModule(UIManagerModule::class.java)!!
14
+
15
+ fun Context.isScreenReaderOn() =
16
+ (getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager).isEnabled
@@ -13,10 +13,12 @@ import android.graphics.drawable.ShapeDrawable
13
13
  import android.graphics.drawable.shapes.RectShape
14
14
  import android.os.Build
15
15
  import android.util.TypedValue
16
+ import android.view.KeyEvent
16
17
  import android.view.MotionEvent
17
18
  import android.view.View
18
19
  import android.view.View.OnClickListener
19
20
  import android.view.ViewGroup
21
+ import android.view.ViewParent
20
22
  import androidx.core.view.children
21
23
  import com.facebook.react.module.annotations.ReactModule
22
24
  import com.facebook.react.uimanager.PixelUtil
@@ -119,6 +121,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
119
121
  private var needBackgroundUpdate = false
120
122
  private var lastEventTime = -1L
121
123
  private var lastAction = -1
124
+ private var receivedKeyEvent = false
122
125
 
123
126
  var isTouched = false
124
127
 
@@ -128,6 +131,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
128
131
  isClickable = true
129
132
  isFocusable = true
130
133
  needBackgroundUpdate = true
134
+ clipChildren = false
131
135
  }
132
136
 
133
137
  private inline fun withBackgroundUpdate(block: () -> Unit) {
@@ -170,6 +174,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
170
174
  override fun onTouchEvent(event: MotionEvent): Boolean {
171
175
  if (event.action == MotionEvent.ACTION_CANCEL) {
172
176
  tryFreeingResponder()
177
+ return super.onTouchEvent(event)
173
178
  }
174
179
 
175
180
  val eventTime = event.eventTime
@@ -208,7 +213,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
208
213
  // Therefore it might be used as long as:
209
214
  // 1. ReactViewManager is not a generic class with a possibility to handle another ViewGroup
210
215
  // 2. There's no way to force native behavior of ReactViewGroup's superclass's onTouchEvent
211
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && selectable is RippleDrawable) {
216
+ if (selectable is RippleDrawable) {
212
217
  val mask = PaintDrawable(Color.WHITE)
213
218
  mask.setCornerRadius(borderRadius)
214
219
  selectable.setDrawableByLayerId(android.R.id.mask, mask)
@@ -235,16 +240,8 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
235
240
  }
236
241
 
237
242
  private fun createSelectableDrawable(): Drawable? {
238
- // TODO: remove once support for RN 0.63 is dropped, since 0.64 minSdkVersion is 21
239
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
240
- context.theme.resolveAttribute(android.R.attr.selectableItemBackground, resolveOutValue, true)
241
- @Suppress("Deprecation")
242
- return resources.getDrawable(resolveOutValue.resourceId)
243
- }
244
-
245
- // Since Android 13, alpha channel in RippleDrawable is clamped between [128, 255]
246
- // see https://github.com/aosp-mirror/platform_frameworks_base/blob/c1bd0480261460584753508327ca8a0c6fc80758/graphics/java/android/graphics/drawable/RippleDrawable.java#L1012
247
- if (rippleColor == Color.TRANSPARENT && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
243
+ // don't create ripple drawable at all when it's not supposed to be visible
244
+ if (rippleColor == Color.TRANSPARENT) {
248
245
  return null
249
246
  }
250
247
 
@@ -333,13 +330,30 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
333
330
  return false
334
331
  }
335
332
 
333
+ override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
334
+ receivedKeyEvent = true
335
+ return super.onKeyUp(keyCode, event)
336
+ }
337
+
336
338
  override fun performClick(): Boolean {
337
339
  // don't preform click when a child button is pressed (mainly to prevent sound effect of
338
340
  // a parent button from playing)
339
- return if (!isChildTouched() && soundResponder == this) {
340
- tryFreeingResponder()
341
- soundResponder = null
342
- super.performClick()
341
+ return if (!isChildTouched()) {
342
+
343
+ if (context.isScreenReaderOn()) {
344
+ findGestureHandlerRootView()?.activateNativeHandlers(this)
345
+ } else if (receivedKeyEvent) {
346
+ findGestureHandlerRootView()?.activateNativeHandlers(this)
347
+ receivedKeyEvent = false
348
+ }
349
+
350
+ if (soundResponder === this) {
351
+ tryFreeingResponder()
352
+ soundResponder = null
353
+ super.performClick()
354
+ } else {
355
+ false
356
+ }
343
357
  } else {
344
358
  false
345
359
  }
@@ -356,7 +370,6 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
356
370
  soundResponder = this
357
371
  }
358
372
  }
359
-
360
373
  // button can be pressed alongside other button if both are non-exclusive and it doesn't have
361
374
  // any pressed children (to prevent pressing the parent when children is pressed).
362
375
  val canBePressedAlongsideOther = !exclusive && touchResponder?.exclusive != true && !isChildTouched()
@@ -378,6 +391,20 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
378
391
  // by default Viewgroup would pass hotspot change events
379
392
  }
380
393
 
394
+ private fun findGestureHandlerRootView(): RNGestureHandlerRootView? {
395
+ var parent: ViewParent? = this.parent
396
+ var gestureHandlerRootView: RNGestureHandlerRootView? = null
397
+
398
+ while (parent != null) {
399
+ if (parent is RNGestureHandlerRootView) {
400
+ gestureHandlerRootView = parent
401
+ }
402
+ parent = parent.parent
403
+ }
404
+
405
+ return gestureHandlerRootView
406
+ }
407
+
381
408
  companion object {
382
409
  var resolveOutValue = TypedValue()
383
410
  var touchResponder: ButtonViewGroup? = null
@@ -20,6 +20,7 @@ import com.swmansion.gesturehandler.BuildConfig
20
20
  import com.swmansion.gesturehandler.ReanimatedEventDispatcher
21
21
  import com.swmansion.gesturehandler.core.FlingGestureHandler
22
22
  import com.swmansion.gesturehandler.core.GestureHandler
23
+ import com.swmansion.gesturehandler.core.HoverGestureHandler
23
24
  import com.swmansion.gesturehandler.core.LongPressGestureHandler
24
25
  import com.swmansion.gesturehandler.core.ManualGestureHandler
25
26
  import com.swmansion.gesturehandler.core.NativeViewGestureHandler
@@ -337,6 +338,25 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
337
338
  }
338
339
  }
339
340
 
341
+ private class HoverGestureHandlerFactory : HandlerFactory<HoverGestureHandler>() {
342
+ override val type = HoverGestureHandler::class.java
343
+ override val name = "HoverGestureHandler"
344
+
345
+ override fun create(context: Context?): HoverGestureHandler {
346
+ return HoverGestureHandler()
347
+ }
348
+
349
+ override fun extractEventData(handler: HoverGestureHandler, eventData: WritableMap) {
350
+ super.extractEventData(handler, eventData)
351
+ with(eventData) {
352
+ putDouble("x", PixelUtil.toDIPFromPixel(handler.lastRelativePositionX).toDouble())
353
+ putDouble("y", PixelUtil.toDIPFromPixel(handler.lastRelativePositionY).toDouble())
354
+ putDouble("absoluteX", PixelUtil.toDIPFromPixel(handler.lastPositionInWindowX).toDouble())
355
+ putDouble("absoluteY", PixelUtil.toDIPFromPixel(handler.lastPositionInWindowY).toDouble())
356
+ }
357
+ }
358
+ }
359
+
340
360
  private val eventListener = object : OnTouchEventListener {
341
361
  override fun <T : GestureHandler<T>> onHandlerUpdate(handler: T, event: MotionEvent) {
342
362
  this@RNGestureHandlerModule.onHandlerUpdate(handler)
@@ -359,6 +379,7 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
359
379
  RotationGestureHandlerFactory(),
360
380
  FlingGestureHandlerFactory(),
361
381
  ManualGestureHandlerFactory(),
382
+ HoverGestureHandlerFactory(),
362
383
  )
363
384
  val registry: RNGestureHandlerRegistry = RNGestureHandlerRegistry()
364
385
  private val interactionManager = RNGestureHandlerInteractionManager()