react-native-gesture-handler 2.6.2 → 2.7.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 (30) hide show
  1. package/android/build.gradle +0 -8
  2. package/android/gradle.properties +1 -1
  3. package/android/lib/src/main/java/com/swmansion/gesturehandler/FlingGestureHandler.kt +7 -7
  4. package/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandler.kt +46 -26
  5. package/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.kt +76 -29
  6. package/android/lib/src/main/java/com/swmansion/gesturehandler/GestureUtils.kt +4 -6
  7. package/android/lib/src/main/java/com/swmansion/gesturehandler/LongPressGestureHandler.kt +6 -6
  8. package/android/lib/src/main/java/com/swmansion/gesturehandler/ManualGestureHandler.kt +1 -1
  9. package/android/lib/src/main/java/com/swmansion/gesturehandler/NativeViewGestureHandler.kt +1 -1
  10. package/android/lib/src/main/java/com/swmansion/gesturehandler/PanGestureHandler.kt +11 -11
  11. package/android/lib/src/main/java/com/swmansion/gesturehandler/PinchGestureHandler.kt +17 -9
  12. package/android/lib/src/main/java/com/swmansion/gesturehandler/RotationGestureHandler.kt +15 -8
  13. package/android/lib/src/main/java/com/swmansion/gesturehandler/TapGestureHandler.kt +10 -10
  14. package/android/noreanimated/src/main/java/com/swmansion/gesturehandler/ReanimatedEventDispatcher.kt +1 -1
  15. package/android/reanimated/src/main/java/com/swmansion/gesturehandler/ReanimatedEventDispatcher.kt +1 -1
  16. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +31 -12
  17. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +2 -2
  18. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt +1 -1
  19. package/ios/RNGestureHandlerButtonComponentView.mm +4 -4
  20. package/lib/commonjs/web/handlers/GestureHandler.js +1 -1
  21. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  22. package/lib/commonjs/web/tools/PointerTracker.js +2 -2
  23. package/lib/commonjs/web/tools/PointerTracker.js.map +1 -1
  24. package/lib/module/web/handlers/GestureHandler.js +1 -1
  25. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  26. package/lib/module/web/tools/PointerTracker.js +2 -2
  27. package/lib/module/web/tools/PointerTracker.js.map +1 -1
  28. package/package.json +4 -8
  29. package/src/web/handlers/GestureHandler.ts +1 -0
  30. package/src/web/tools/PointerTracker.ts +2 -2
@@ -212,14 +212,6 @@ dependencies {
212
212
  }
213
213
 
214
214
  if (isNewArchitectureEnabled()) {
215
- react {
216
- reactNativeDir = rootProject.file(findNodeModulePath(rootProject.rootDir, "react-native") ?: "../node_modules/react-native/")
217
- jsRootDir = file("../src/fabric/")
218
- codegenDir = rootProject.file(findNodeModulePath(rootProject.rootDir, "react-native-codegen") ?: "../node_modules/react-native-codegen/")
219
- libraryName = "rngesturehandler"
220
- codegenJavaPackageName = "com.swmansion.gesturehandler"
221
- }
222
-
223
215
  // Resolves "LOCAL_SRC_FILES points to a missing file, Check that libfb.so exists or that its path is correct".
224
216
  tasks.whenTaskAdded { task ->
225
217
  if (task.name.contains("configureCMakeDebug")) {
@@ -16,4 +16,4 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
16
16
  # This option should only be used with decoupled projects. More details, visit
17
17
  # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18
18
  # org.gradle.parallel=true
19
- RNGH_kotlinVersion=1.5.20
19
+ RNGH_kotlinVersion=1.6.21
@@ -63,19 +63,19 @@ class FlingGestureHandler : GestureHandler<FlingGestureHandler>() {
63
63
  }
64
64
  }
65
65
 
66
- override fun onHandle(event: MotionEvent) {
66
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
67
67
  val state = state
68
68
  if (state == STATE_UNDETERMINED) {
69
- startFling(event)
69
+ startFling(sourceEvent)
70
70
  }
71
71
  if (state == STATE_BEGAN) {
72
- tryEndFling(event)
73
- if (event.pointerCount > maxNumberOfPointersSimultaneously) {
74
- maxNumberOfPointersSimultaneously = event.pointerCount
72
+ tryEndFling(sourceEvent)
73
+ if (sourceEvent.pointerCount > maxNumberOfPointersSimultaneously) {
74
+ maxNumberOfPointersSimultaneously = sourceEvent.pointerCount
75
75
  }
76
- val action = event.actionMasked
76
+ val action = sourceEvent.actionMasked
77
77
  if (action == MotionEvent.ACTION_UP) {
78
- endFling(event)
78
+ endFling(sourceEvent)
79
79
  }
80
80
  }
81
81
  }
@@ -3,6 +3,7 @@ package com.swmansion.gesturehandler
3
3
  import android.app.Activity
4
4
  import android.content.Context
5
5
  import android.content.ContextWrapper
6
+ import android.graphics.PointF
6
7
  import android.graphics.Rect
7
8
  import android.view.MotionEvent
8
9
  import android.view.MotionEvent.PointerCoords
@@ -325,7 +326,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
325
326
  while handling event: $event
326
327
  """.trimIndent(), e) {}
327
328
 
328
- fun handle(origEvent: MotionEvent) {
329
+ fun handle(transformedEvent: MotionEvent, sourceEvent: MotionEvent) {
329
330
  if (!isEnabled
330
331
  || state == STATE_CANCELLED
331
332
  || state == STATE_FAILED
@@ -335,20 +336,20 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
335
336
  }
336
337
 
337
338
  // a workaround for https://github.com/software-mansion/react-native-gesture-handler/issues/1188
338
- val event = if (BuildConfig.DEBUG) {
339
- adaptEvent(origEvent)
339
+ val (adaptedTransformedEvent, adaptedSourceEvent) = if (BuildConfig.DEBUG) {
340
+ arrayOf(adaptEvent(transformedEvent), adaptEvent(sourceEvent))
340
341
  } else {
341
342
  try {
342
- adaptEvent(origEvent)
343
+ arrayOf(adaptEvent(transformedEvent), adaptEvent(sourceEvent))
343
344
  } catch (e: AdaptEventException) {
344
345
  fail()
345
346
  return
346
347
  }
347
348
  }
348
349
 
349
- x = event.x
350
- y = event.y
351
- numberOfPointers = event.pointerCount
350
+ x = adaptedTransformedEvent.x
351
+ y = adaptedTransformedEvent.y
352
+ numberOfPointers = adaptedTransformedEvent.pointerCount
352
353
  isWithinBounds = isWithinBounds(view, x, y)
353
354
  if (shouldCancelWhenOutside && !isWithinBounds) {
354
355
  if (state == STATE_ACTIVE) {
@@ -358,13 +359,16 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
358
359
  }
359
360
  return
360
361
  }
361
- lastAbsolutePositionX = GestureUtils.getLastPointerX(event, true)
362
- lastAbsolutePositionY = GestureUtils.getLastPointerY(event, true)
363
- lastEventOffsetX = event.rawX - event.x
364
- lastEventOffsetY = event.rawY - event.y
365
- onHandle(event)
366
- if (event != origEvent) {
367
- event.recycle()
362
+ lastAbsolutePositionX = GestureUtils.getLastPointerX(adaptedTransformedEvent, true)
363
+ lastAbsolutePositionY = GestureUtils.getLastPointerY(adaptedTransformedEvent, true)
364
+ lastEventOffsetX = adaptedTransformedEvent.rawX - adaptedTransformedEvent.x
365
+ lastEventOffsetY = adaptedTransformedEvent.rawY - adaptedTransformedEvent.y
366
+ onHandle(adaptedTransformedEvent, adaptedSourceEvent)
367
+ if (adaptedTransformedEvent != transformedEvent) {
368
+ adaptedTransformedEvent.recycle()
369
+ }
370
+ if (adaptedSourceEvent != sourceEvent) {
371
+ adaptedSourceEvent.recycle()
368
372
  }
369
373
  }
370
374
 
@@ -585,11 +589,11 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
585
589
  var top = 0f
586
590
  var right = view!!.width.toFloat()
587
591
  var bottom = view.height.toFloat()
588
- if (hitSlop != null) {
589
- val padLeft = hitSlop!![HIT_SLOP_LEFT_IDX]
590
- val padTop = hitSlop!![HIT_SLOP_TOP_IDX]
591
- val padRight = hitSlop!![HIT_SLOP_RIGHT_IDX]
592
- val padBottom = hitSlop!![HIT_SLOP_BOTTOM_IDX]
592
+ hitSlop?.let { hitSlop ->
593
+ val padLeft = hitSlop[HIT_SLOP_LEFT_IDX]
594
+ val padTop = hitSlop[HIT_SLOP_TOP_IDX]
595
+ val padRight = hitSlop[HIT_SLOP_RIGHT_IDX]
596
+ val padBottom = hitSlop[HIT_SLOP_BOTTOM_IDX]
593
597
  if (hitSlopSet(padLeft)) {
594
598
  left -= padLeft
595
599
  }
@@ -602,8 +606,8 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
602
606
  if (hitSlopSet(padBottom)) {
603
607
  bottom += padBottom
604
608
  }
605
- val width = hitSlop!![HIT_SLOP_WIDTH_IDX]
606
- val height = hitSlop!![HIT_SLOP_HEIGHT_IDX]
609
+ val width = hitSlop[HIT_SLOP_WIDTH_IDX]
610
+ val height = hitSlop[HIT_SLOP_HEIGHT_IDX]
607
611
  if (hitSlopSet(width)) {
608
612
  if (!hitSlopSet(padLeft)) {
609
613
  left = right - width
@@ -659,13 +663,29 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
659
663
  // if the handler is waiting for failure of other one)
660
664
  open fun resetProgress() {}
661
665
 
662
- protected open fun onHandle(event: MotionEvent) {
666
+ protected open fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
663
667
  moveToState(STATE_FAILED)
664
668
  }
665
669
 
666
670
  protected open fun onStateChange(newState: Int, previousState: Int) {}
667
671
  protected open fun onReset() {}
668
672
  protected open fun onCancel() {}
673
+
674
+ /**
675
+ * Transforms a point in the coordinate space of the wrapperView (GestureHandlerRootView) to
676
+ * coordinate space of the view the gesture is attached to.
677
+ *
678
+ * If the gesture handler is not currently attached to a view, it will return (NaN, NaN).
679
+ *
680
+ * This method modifies and transforms the received point.
681
+ */
682
+ protected fun transformPoint(point: PointF): PointF {
683
+ return orchestrator?.transformPointToViewCoords(this.view, point) ?: run {
684
+ point.x = Float.NaN
685
+ point.y = Float.NaN
686
+ point
687
+ }
688
+ }
669
689
  fun reset() {
670
690
  view = null
671
691
  orchestrator = null
@@ -689,14 +709,14 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
689
709
  }
690
710
 
691
711
  val lastRelativePositionX: Float
692
- get() = lastAbsolutePositionX - lastEventOffsetX
712
+ get() = lastAbsolutePositionX
693
713
  val lastRelativePositionY: Float
694
- get() = lastAbsolutePositionY - lastEventOffsetY
714
+ get() = lastAbsolutePositionY
695
715
 
696
716
  val lastPositionInWindowX: Float
697
- get() = lastAbsolutePositionX - windowOffset[0]
717
+ get() = lastAbsolutePositionX + lastEventOffsetX - windowOffset[0]
698
718
  val lastPositionInWindowY: Float
699
- get() = lastAbsolutePositionY - windowOffset[1]
719
+ get() = lastAbsolutePositionY + lastEventOffsetY - windowOffset[1]
700
720
 
701
721
  companion object {
702
722
  const val STATE_UNDETERMINED = 0
@@ -242,7 +242,7 @@ class GestureHandlerOrchestrator(
242
242
  }
243
243
  }
244
244
 
245
- private fun deliverEventToGestureHandler(handler: GestureHandler<*>, event: MotionEvent) {
245
+ private fun deliverEventToGestureHandler(handler: GestureHandler<*>, sourceEvent: MotionEvent) {
246
246
  if (!isViewAttachedUnderWrapper(handler.view)) {
247
247
  handler.cancel()
248
248
  return
@@ -250,18 +250,9 @@ class GestureHandlerOrchestrator(
250
250
  if (!handler.wantEvents()) {
251
251
  return
252
252
  }
253
- val action = event.actionMasked
254
- val coords = tempCoords
255
- extractCoordsForView(handler.view, event, coords)
256
- val oldX = event.x
257
- val oldY = event.y
258
- // TODO: we may consider scaling events if necessary using MotionEvent.transform
259
- // for now the events are only offset to the top left corner of the view but if
260
- // view or any ot the parents is scaled the other pointers position will not reflect
261
- // their actual place in the view. On the other hand not scaling seems like a better
262
- // approach when we want to use pointer coordinates to calculate velocity or distance
263
- // for pinch so I don't know yet if we should transform or not...
264
- event.setLocation(coords[0], coords[1])
253
+
254
+ val action = sourceEvent.actionMasked
255
+ val event = transformEventToViewCoords(handler.view, MotionEvent.obtain(sourceEvent))
265
256
 
266
257
  // Touch events are sent before the handler itself has a chance to process them,
267
258
  // mainly because `onTouchesUp` shoul be send befor gesture finishes. This means that
@@ -277,7 +268,7 @@ class GestureHandlerOrchestrator(
277
268
 
278
269
  if (!handler.isAwaiting || action != MotionEvent.ACTION_MOVE) {
279
270
  val isFirstEvent = handler.state == 0
280
- handler.handle(event)
271
+ handler.handle(event, sourceEvent)
281
272
  if (handler.isActive) {
282
273
  // After handler is done waiting for other one to fail its progress should be
283
274
  // reset, otherwise there may be a visible jump in values sent by the handler.
@@ -305,7 +296,7 @@ class GestureHandlerOrchestrator(
305
296
  }
306
297
  }
307
298
 
308
- event.setLocation(oldX, oldY)
299
+ event.recycle()
309
300
  }
310
301
 
311
302
  /**
@@ -329,19 +320,75 @@ class GestureHandlerOrchestrator(
329
320
  return parent === wrapperView
330
321
  }
331
322
 
332
- private fun extractCoordsForView(view: View?, event: MotionEvent, outputCoords: FloatArray) {
333
- if (view === wrapperView) {
334
- outputCoords[0] = event.x
335
- outputCoords[1] = event.y
336
- return
323
+ /**
324
+ * Transforms an event in the coordinates of wrapperView into the coordinate space of the received view.
325
+ *
326
+ * This modifies and returns the same event as it receives
327
+ *
328
+ * @param view - view to which coordinate space the event should be transformed
329
+ * @param event - event to transform
330
+ */
331
+ fun transformEventToViewCoords(view: View?, event: MotionEvent): MotionEvent {
332
+ if (view == null) {
333
+ return event
337
334
  }
338
- require(!(view == null || view.parent !is ViewGroup)) { "Parent is null? View is no longer in the tree" }
339
- val parent = view.parent as ViewGroup
340
- extractCoordsForView(parent, event, outputCoords)
341
- val childPoint = tempPoint
342
- transformTouchPointToViewCoords(outputCoords[0], outputCoords[1], parent, view, childPoint)
343
- outputCoords[0] = childPoint.x
344
- outputCoords[1] = childPoint.y
335
+
336
+ val parent = view.parent as? ViewGroup
337
+ // Events are passed down to the orchestrator by the wrapperView, so they are already in the
338
+ // relevant coordinate space. We want to stop traversing the tree when we reach it.
339
+ if (parent != wrapperView) {
340
+ transformEventToViewCoords(parent, event)
341
+ }
342
+
343
+ if (parent != null) {
344
+ val localX = event.x + parent.scrollX - view.left
345
+ val localY = event.y + parent.scrollY - view.top
346
+ event.setLocation(localX, localY)
347
+ }
348
+
349
+ if (!view.matrix.isIdentity) {
350
+ view.matrix.invert(inverseMatrix)
351
+ event.transform(inverseMatrix)
352
+ }
353
+
354
+ return event
355
+ }
356
+
357
+ /**
358
+ * Transforms a point in the coordinates of wrapperView into the coordinate space of the received view.
359
+ *
360
+ * This modifies and returns the same point as it receives
361
+ *
362
+ * @param view - view to which coordinate space the point should be transformed
363
+ * @param point - point to transform
364
+ */
365
+ fun transformPointToViewCoords(view: View?, point: PointF): PointF {
366
+ if (view == null) {
367
+ return point
368
+ }
369
+
370
+ val parent = view.parent as? ViewGroup
371
+ // Events are passed down to the orchestrator by the wrapperView, so they are already in the
372
+ // relevant coordinate space. We want to stop traversing the tree when we reach it.
373
+ if (parent != wrapperView) {
374
+ transformPointToViewCoords(parent, point)
375
+ }
376
+
377
+ if (parent != null) {
378
+ point.x += parent.scrollX - view.left
379
+ point.y += parent.scrollY - view.top
380
+ }
381
+
382
+ if (!view.matrix.isIdentity) {
383
+ view.matrix.invert(inverseMatrix)
384
+ tempCoords[0] = point.x
385
+ tempCoords[1] = point.y
386
+ inverseMatrix.mapPoints(tempCoords)
387
+ point.x = tempCoords[0]
388
+ point.y = tempCoords[1]
389
+ }
390
+
391
+ return point
345
392
  }
346
393
 
347
394
  private fun addAwaitingHandler(handler: GestureHandler<*>) {
@@ -451,7 +498,7 @@ class GestureHandlerOrchestrator(
451
498
  val child = viewConfigHelper.getChildInDrawingOrderAtIndex(viewGroup, i)
452
499
  if (canReceiveEvents(child)) {
453
500
  val childPoint = tempPoint
454
- transformTouchPointToViewCoords(coords[0], coords[1], viewGroup, child, childPoint)
501
+ transformPointToChildViewCoords(coords[0], coords[1], viewGroup, child, childPoint)
455
502
  val restoreX = coords[0]
456
503
  val restoreY = coords[1]
457
504
  coords[0] = childPoint.x
@@ -564,7 +611,7 @@ class GestureHandlerOrchestrator(
564
611
  return isLeafOrTransparent && isTransformedTouchPointInView(coords[0], coords[1], view)
565
612
  }
566
613
 
567
- private fun transformTouchPointToViewCoords(
614
+ private fun transformPointToChildViewCoords(
568
615
  x: Float,
569
616
  y: Float,
570
617
  parent: ViewGroup,
@@ -4,14 +4,13 @@ import android.view.MotionEvent
4
4
 
5
5
  object GestureUtils {
6
6
  fun getLastPointerX(event: MotionEvent, averageTouches: Boolean): Float {
7
- val offset = event.rawX - event.x
8
7
  val excludeIndex = if (event.actionMasked == MotionEvent.ACTION_POINTER_UP) event.actionIndex else -1
9
8
  return if (averageTouches) {
10
9
  var sum = 0f
11
10
  var count = 0
12
11
  for (i in 0 until event.pointerCount) {
13
12
  if (i != excludeIndex) {
14
- sum += event.getX(i) + offset
13
+ sum += event.getX(i)
15
14
  count++
16
15
  }
17
16
  }
@@ -21,19 +20,18 @@ object GestureUtils {
21
20
  if (lastPointerIdx == excludeIndex) {
22
21
  lastPointerIdx--
23
22
  }
24
- event.getX(lastPointerIdx) + offset
23
+ event.getX(lastPointerIdx)
25
24
  }
26
25
  }
27
26
 
28
27
  fun getLastPointerY(event: MotionEvent, averageTouches: Boolean): Float {
29
- val offset = event.rawY - event.y
30
28
  val excludeIndex = if (event.actionMasked == MotionEvent.ACTION_POINTER_UP) event.actionIndex else -1
31
29
  return if (averageTouches) {
32
30
  var sum = 0f
33
31
  var count = 0
34
32
  for (i in 0 until event.pointerCount) {
35
33
  if (i != excludeIndex) {
36
- sum += event.getY(i) + offset
34
+ sum += event.getY(i)
37
35
  count++
38
36
  }
39
37
  }
@@ -43,7 +41,7 @@ object GestureUtils {
43
41
  if (lastPointerIdx == excludeIndex) {
44
42
  lastPointerIdx -= 1
45
43
  }
46
- event.getY(lastPointerIdx) + offset
44
+ event.getY(lastPointerIdx)
47
45
  }
48
46
  }
49
47
  }
@@ -37,13 +37,13 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
37
37
  return this
38
38
  }
39
39
 
40
- override fun onHandle(event: MotionEvent) {
40
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
41
41
  if (state == STATE_UNDETERMINED) {
42
42
  previousTime = SystemClock.uptimeMillis()
43
43
  startTime = previousTime
44
44
  begin()
45
- startX = event.rawX
46
- startY = event.rawY
45
+ startX = sourceEvent.rawX
46
+ startY = sourceEvent.rawY
47
47
  handler = Handler(Looper.getMainLooper())
48
48
  if (minDurationMs > 0) {
49
49
  handler!!.postDelayed({ activate() }, minDurationMs)
@@ -51,7 +51,7 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
51
51
  activate()
52
52
  }
53
53
  }
54
- if (event.actionMasked == MotionEvent.ACTION_UP) {
54
+ if (sourceEvent.actionMasked == MotionEvent.ACTION_UP) {
55
55
  handler?.let {
56
56
  it.removeCallbacksAndMessages(null)
57
57
  handler = null
@@ -63,8 +63,8 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
63
63
  }
64
64
  } else {
65
65
  // calculate distance from start
66
- val deltaX = event.rawX - startX
67
- val deltaY = event.rawY - startY
66
+ val deltaX = sourceEvent.rawX - startX
67
+ val deltaY = sourceEvent.rawY - startY
68
68
  val distSq = deltaX * deltaX + deltaY * deltaY
69
69
  if (distSq > maxDistSq) {
70
70
  if (state == STATE_ACTIVE) {
@@ -3,7 +3,7 @@ package com.swmansion.gesturehandler
3
3
  import android.view.MotionEvent
4
4
 
5
5
  class ManualGestureHandler : GestureHandler<ManualGestureHandler>() {
6
- override fun onHandle(event: MotionEvent) {
6
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
7
7
  if (state == STATE_UNDETERMINED) {
8
8
  begin()
9
9
  }
@@ -75,7 +75,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
75
75
  }
76
76
  }
77
77
 
78
- override fun onHandle(event: MotionEvent) {
78
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
79
79
  val view = view!!
80
80
  if (event.actionMasked == MotionEvent.ACTION_UP) {
81
81
  view.onTouchEvent(event)
@@ -205,31 +205,31 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
205
205
  return failOffsetYEnd != MIN_VALUE_IGNORE && dy > failOffsetYEnd
206
206
  }
207
207
 
208
- override fun onHandle(event: MotionEvent) {
208
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
209
209
  val state = state
210
- val action = event.actionMasked
210
+ val action = sourceEvent.actionMasked
211
211
  if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN) {
212
212
  // update offset if new pointer gets added or removed
213
213
  offsetX += lastX - startX
214
214
  offsetY += lastY - startY
215
215
 
216
216
  // reset starting point
217
- lastX = getLastPointerX(event, averageTouches)
218
- lastY = getLastPointerY(event, averageTouches)
217
+ lastX = getLastPointerX(sourceEvent, averageTouches)
218
+ lastY = getLastPointerY(sourceEvent, averageTouches)
219
219
  startX = lastX
220
220
  startY = lastY
221
221
  } else {
222
- lastX = getLastPointerX(event, averageTouches)
223
- lastY = getLastPointerY(event, averageTouches)
222
+ lastX = getLastPointerX(sourceEvent, averageTouches)
223
+ lastY = getLastPointerY(sourceEvent, averageTouches)
224
224
  }
225
- if (state == STATE_UNDETERMINED && event.pointerCount >= minPointers) {
225
+ if (state == STATE_UNDETERMINED && sourceEvent.pointerCount >= minPointers) {
226
226
  resetProgress()
227
227
  offsetX = 0f
228
228
  offsetY = 0f
229
229
  velocityX = 0f
230
230
  velocityY = 0f
231
231
  velocityTracker = VelocityTracker.obtain()
232
- addVelocityMovement(velocityTracker, event)
232
+ addVelocityMovement(velocityTracker, sourceEvent)
233
233
  begin()
234
234
 
235
235
  if (activateAfterLongPress > 0) {
@@ -239,7 +239,7 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
239
239
  handler!!.postDelayed(activateDelayed, activateAfterLongPress)
240
240
  }
241
241
  } else if (velocityTracker != null) {
242
- addVelocityMovement(velocityTracker, event)
242
+ addVelocityMovement(velocityTracker, sourceEvent)
243
243
  velocityTracker!!.computeCurrentVelocity(1000)
244
244
  velocityX = velocityTracker!!.xVelocity
245
245
  velocityY = velocityTracker!!.yVelocity
@@ -250,14 +250,14 @@ class PanGestureHandler(context: Context?) : GestureHandler<PanGestureHandler>()
250
250
  } else {
251
251
  fail()
252
252
  }
253
- } else if (action == MotionEvent.ACTION_POINTER_DOWN && event.pointerCount > maxPointers) {
253
+ } else if (action == MotionEvent.ACTION_POINTER_DOWN && sourceEvent.pointerCount > maxPointers) {
254
254
  // When new finger is placed down (POINTER_DOWN) we check if MAX_POINTERS is not exceeded
255
255
  if (state == STATE_ACTIVE) {
256
256
  cancel()
257
257
  } else {
258
258
  fail()
259
259
  }
260
- } else if (action == MotionEvent.ACTION_POINTER_UP && state == STATE_ACTIVE && event.pointerCount < minPointers) {
260
+ } else if (action == MotionEvent.ACTION_POINTER_UP && state == STATE_ACTIVE && sourceEvent.pointerCount < minPointers) {
261
261
  // When finger is lifted up (POINTER_UP) and the number of pointers falls below MIN_POINTERS
262
262
  // threshold, we only want to take an action when the handler has already activated. Otherwise
263
263
  // we can still expect more fingers to be placed on screen and fulfill MIN_POINTERS criteria.
@@ -1,5 +1,6 @@
1
1
  package com.swmansion.gesturehandler
2
2
 
3
+ import android.graphics.PointF
3
4
  import android.view.MotionEvent
4
5
  import android.view.ViewConfiguration
5
6
  import kotlin.math.abs
@@ -9,10 +10,10 @@ class PinchGestureHandler : GestureHandler<PinchGestureHandler>() {
9
10
  private set
10
11
  var velocity = 0.0
11
12
  private set
12
- val focalPointX: Float
13
- get() = scaleGestureDetector?.focusX ?: Float.NaN
14
- val focalPointY: Float
15
- get() = scaleGestureDetector?.focusY ?: Float.NaN
13
+ var focalPointX: Float = Float.NaN
14
+ private set
15
+ var focalPointY: Float = Float.NaN
16
+ private set
16
17
 
17
18
  private var scaleGestureDetector: ScaleGestureDetector? = null
18
19
  private var startingSpan = 0f
@@ -47,7 +48,7 @@ class PinchGestureHandler : GestureHandler<PinchGestureHandler>() {
47
48
  }
48
49
  }
49
50
 
50
- override fun onHandle(event: MotionEvent) {
51
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
51
52
  if (state == STATE_UNDETERMINED) {
52
53
  val context = view!!.context
53
54
  resetProgress()
@@ -56,14 +57,19 @@ class PinchGestureHandler : GestureHandler<PinchGestureHandler>() {
56
57
  spanSlop = configuration.scaledTouchSlop.toFloat()
57
58
  begin()
58
59
  }
59
- scaleGestureDetector?.onTouchEvent(event)
60
- var activePointers = event.pointerCount
61
- if (event.actionMasked == MotionEvent.ACTION_POINTER_UP) {
60
+ scaleGestureDetector?.onTouchEvent(sourceEvent)
61
+ scaleGestureDetector?.let {
62
+ val point = transformPoint(PointF(it.focusX, it.focusY))
63
+ this.focalPointX = point.x
64
+ this.focalPointY = point.y
65
+ }
66
+ var activePointers = sourceEvent.pointerCount
67
+ if (sourceEvent.actionMasked == MotionEvent.ACTION_POINTER_UP) {
62
68
  activePointers -= 1
63
69
  }
64
70
  if (state == STATE_ACTIVE && activePointers < 2) {
65
71
  end()
66
- } else if (event.actionMasked == MotionEvent.ACTION_UP) {
72
+ } else if (sourceEvent.actionMasked == MotionEvent.ACTION_UP) {
67
73
  fail()
68
74
  }
69
75
  }
@@ -78,6 +84,8 @@ class PinchGestureHandler : GestureHandler<PinchGestureHandler>() {
78
84
 
79
85
  override fun onReset() {
80
86
  scaleGestureDetector = null
87
+ focalPointX = Float.NaN
88
+ focalPointY = Float.NaN
81
89
  resetProgress()
82
90
  }
83
91
 
@@ -1,5 +1,6 @@
1
1
  package com.swmansion.gesturehandler
2
2
 
3
+ import android.graphics.PointF
3
4
  import android.view.MotionEvent
4
5
  import com.swmansion.gesturehandler.RotationGestureDetector.OnRotationGestureListener
5
6
  import kotlin.math.abs
@@ -10,11 +11,10 @@ class RotationGestureHandler : GestureHandler<RotationGestureHandler>() {
10
11
  private set
11
12
  var velocity = 0.0
12
13
  private set
13
-
14
- val anchorX: Float
15
- get() = rotationGestureDetector?.anchorX ?: Float.NaN
16
- val anchorY: Float
17
- get() = rotationGestureDetector?.anchorY ?: Float.NaN
14
+ var anchorX: Float = Float.NaN
15
+ private set
16
+ var anchorY: Float = Float.NaN
17
+ private set
18
18
 
19
19
  init {
20
20
  setShouldCancelWhenOutside(false)
@@ -41,14 +41,19 @@ class RotationGestureHandler : GestureHandler<RotationGestureHandler>() {
41
41
  }
42
42
  }
43
43
 
44
- override fun onHandle(event: MotionEvent) {
44
+ override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
45
45
  if (state == STATE_UNDETERMINED) {
46
46
  resetProgress()
47
47
  rotationGestureDetector = RotationGestureDetector(gestureListener)
48
48
  begin()
49
49
  }
50
- rotationGestureDetector?.onTouchEvent(event)
51
- if (event.actionMasked == MotionEvent.ACTION_UP) {
50
+ rotationGestureDetector?.onTouchEvent(sourceEvent)
51
+ rotationGestureDetector?.let {
52
+ val point = transformPoint(PointF(it.anchorX, it.anchorY))
53
+ anchorX = point.x
54
+ anchorY = point.y
55
+ }
56
+ if (sourceEvent.actionMasked == MotionEvent.ACTION_UP) {
52
57
  if (state == STATE_ACTIVE) {
53
58
  end()
54
59
  } else {
@@ -67,6 +72,8 @@ class RotationGestureHandler : GestureHandler<RotationGestureHandler>() {
67
72
 
68
73
  override fun onReset() {
69
74
  rotationGestureDetector = null
75
+ anchorX = Float.NaN
76
+ anchorY = Float.NaN
70
77
  resetProgress()
71
78
  }
72
79