Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import android.view.VelocityTracker
import com.facebook.react.bridge.ReadableMap
import com.swmansion.gesturehandler.react.eventbuilders.FlingGestureHandlerEventDataBuilder

class FlingGestureHandler : GestureHandler<FlingGestureHandler>() {
class FlingGestureHandler : GestureHandler() {
var numberOfPointersRequired = DEFAULT_NUMBER_OF_TOUCHES_REQUIRED
var direction = DEFAULT_DIRECTION

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.swmansion.gesturehandler.react.eventbuilders.GestureHandlerEventDataB
import java.lang.IllegalStateException
import java.util.*

open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestureHandlerT>> {
open class GestureHandler {
private val trackedPointerIDs = IntArray(MAX_POINTERS_COUNT)
private var trackedPointersIDsCount = 0
private val windowOffset = IntArray(2) { 0 }
Expand Down Expand Up @@ -79,37 +79,30 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
protected set
protected var shouldCancelWhenOutside = false
protected var orchestrator: GestureHandlerOrchestrator? = null
private var onTouchEventListener: OnTouchEventListener? = null
var onTouchEventListener: OnTouchEventListener? = null
private var interactionController: GestureHandlerInteractionController? = null
var pointerType: Int = POINTER_TYPE_OTHER
private set

protected var mouseButton = 0

@Suppress("UNCHECKED_CAST")
protected fun self(): ConcreteGestureHandlerT = this as ConcreteGestureHandlerT

protected inline fun applySelf(block: ConcreteGestureHandlerT.() -> Unit): ConcreteGestureHandlerT = self().apply {
block()
}

// properties set and accessed only by the orchestrator
var activationIndex = 0
var isActive = false
var isAwaiting = false
var shouldResetProgress = false

open fun dispatchStateChange(newState: Int, prevState: Int) {
onTouchEventListener?.onStateChange(self(), newState, prevState)
onTouchEventListener?.onStateChange(this, newState, prevState)
}

open fun dispatchHandlerUpdate(event: MotionEvent) {
onTouchEventListener?.onHandlerUpdate(self(), event)
onTouchEventListener?.onHandlerUpdate(this, event)
}

open fun dispatchTouchEvent() {
if (changedTouchesPayload != null) {
onTouchEventListener?.onTouchEvent(self())
onTouchEventListener?.onTouchEvent(this)
}
}

Expand All @@ -122,7 +115,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
mouseButton = DEFAULT_MOUSE_BUTTON
}

fun hasCommonPointers(other: GestureHandler<*>): Boolean {
fun hasCommonPointers(other: GestureHandler): Boolean {
for (i in trackedPointerIDs.indices) {
if (trackedPointerIDs[i] != -1 && other.trackedPointerIDs[i] != -1) {
return true
Expand All @@ -131,14 +124,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
return false
}

fun setHitSlop(
leftPad: Float,
topPad: Float,
rightPad: Float,
bottomPad: Float,
width: Float,
height: Float,
): ConcreteGestureHandlerT = applySelf {
fun setHitSlop(leftPad: Float, topPad: Float, rightPad: Float, bottomPad: Float, width: Float, height: Float) {
if (hitSlop == null) {
hitSlop = FloatArray(6)
}
Expand All @@ -162,10 +148,9 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
}
}

fun setHitSlop(padding: Float): ConcreteGestureHandlerT =
setHitSlop(padding, padding, padding, padding, HIT_SLOP_NONE, HIT_SLOP_NONE)
fun setHitSlop(padding: Float) = setHitSlop(padding, padding, padding, padding, HIT_SLOP_NONE, HIT_SLOP_NONE)

fun setInteractionController(controller: GestureHandlerInteractionController?): ConcreteGestureHandlerT = applySelf {
fun setInteractionController(controller: GestureHandlerInteractionController?) {
interactionController = controller
}

Expand Down Expand Up @@ -331,7 +316,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
}

// exception to help debug https://github.com/software-mansion/react-native-gesture-handler/issues/1188
class AdaptEventException(handler: GestureHandler<*>, event: MotionEvent, e: IllegalArgumentException) :
class AdaptEventException(handler: GestureHandler, event: MotionEvent, e: IllegalArgumentException) :
Exception(
"""
handler: ${handler::class.simpleName}
Expand Down Expand Up @@ -594,31 +579,31 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
state != STATE_END &&
trackedPointersIDsCount > 0

open fun shouldRequireToWaitForFailure(handler: GestureHandler<*>): Boolean {
open fun shouldRequireToWaitForFailure(handler: GestureHandler): Boolean {
if (handler === this) {
return false
}

return interactionController?.shouldRequireHandlerToWaitForFailure(this, handler) ?: false
}

fun shouldWaitForHandlerFailure(handler: GestureHandler<*>): Boolean {
fun shouldWaitForHandlerFailure(handler: GestureHandler): Boolean {
if (handler === this) {
return false
}

return interactionController?.shouldWaitForHandlerFailure(this, handler) ?: false
}

open fun shouldRecognizeSimultaneously(handler: GestureHandler<*>): Boolean {
open fun shouldRecognizeSimultaneously(handler: GestureHandler): Boolean {
if (handler === this) {
return true
}

return interactionController?.shouldRecognizeSimultaneously(this, handler) ?: false
}

open fun shouldBeCancelledBy(handler: GestureHandler<*>): Boolean {
open fun shouldBeCancelledBy(handler: GestureHandler): Boolean {
if (handler === this) {
return false
}
Expand Down Expand Up @@ -714,7 +699,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
* Returns true if the view this handler is attached to is a descendant of the view the other handler
* is attached to and false otherwise.
*/
fun isDescendantOf(of: GestureHandler<*>): Boolean {
fun isDescendantOf(of: GestureHandler): Boolean {
var view = this.view?.parent as? View
while (view != null) {
if (view == of.view) {
Expand Down Expand Up @@ -831,11 +816,6 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
}
}

fun setOnTouchEventListener(listener: OnTouchEventListener?): GestureHandler<*> {
onTouchEventListener = listener
return this
}

override fun toString(): String {
val viewString = if (view == null) null else view!!.javaClass.simpleName
return this.javaClass.simpleName + "@[" + tag + "]:" + viewString
Expand All @@ -851,7 +831,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
val lastPositionInWindowY: Float
get() = lastAbsolutePositionY + lastEventOffsetY - windowOffset[1]

abstract class Factory<T : GestureHandler<T>> {
abstract class Factory<T : GestureHandler> {
abstract val type: Class<T>
abstract val name: String

Expand Down Expand Up @@ -899,7 +879,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
private const val KEY_HIT_SLOP_WIDTH = "width"
private const val KEY_HIT_SLOP_HEIGHT = "height"

private fun handleHitSlopProperty(handler: GestureHandler<*>, config: ReadableMap) {
private fun handleHitSlopProperty(handler: GestureHandler, config: ReadableMap) {
if (config.getType(KEY_HIT_SLOP) == ReadableType.Number) {
val hitSlop = PixelUtil.toPixelFromDIP(config.getDouble(KEY_HIT_SLOP))
handler.setHitSlop(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.swmansion.gesturehandler.core

interface GestureHandlerInteractionController {
fun shouldWaitForHandlerFailure(handler: GestureHandler<*>, otherHandler: GestureHandler<*>): Boolean
fun shouldRequireHandlerToWaitForFailure(handler: GestureHandler<*>, otherHandler: GestureHandler<*>): Boolean
fun shouldRecognizeSimultaneously(handler: GestureHandler<*>, otherHandler: GestureHandler<*>): Boolean
fun shouldHandlerBeCancelledBy(handler: GestureHandler<*>, otherHandler: GestureHandler<*>): Boolean
fun shouldWaitForHandlerFailure(handler: GestureHandler, otherHandler: GestureHandler): Boolean
fun shouldRequireHandlerToWaitForFailure(handler: GestureHandler, otherHandler: GestureHandler): Boolean
fun shouldRecognizeSimultaneously(handler: GestureHandler, otherHandler: GestureHandler): Boolean
fun shouldHandlerBeCancelledBy(handler: GestureHandler, otherHandler: GestureHandler): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ class GestureHandlerOrchestrator(
* traversing view hierarchy and looking for gesture handlers.
*/
var minimumAlphaForTraversal = DEFAULT_MIN_ALPHA_FOR_TRAVERSAL
private val gestureHandlers = arrayListOf<GestureHandler<*>>()
private val awaitingHandlers = arrayListOf<GestureHandler<*>>()
private val preparedHandlers = arrayListOf<GestureHandler<*>>()
private val gestureHandlers = arrayListOf<GestureHandler>()
private val awaitingHandlers = arrayListOf<GestureHandler>()
private val preparedHandlers = arrayListOf<GestureHandler>()

// In `onHandlerStateChange` method we iterate through `awaitingHandlers`, but calling `tryActivate` may modify this list.
// To avoid `ConcurrentModificationException` we iterate through copy. There is one more problem though - if handler was
Expand Down Expand Up @@ -85,22 +85,20 @@ class GestureHandlerOrchestrator(
finishedHandlersCleanupScheduled = false
}

private fun hasOtherHandlerToWaitFor(handler: GestureHandler<*>) = gestureHandlers.any {
!isFinished(it.state) && shouldHandlerWaitForOther(handler, it)
}
private fun hasOtherHandlerToWaitFor(handler: GestureHandler) =
gestureHandlers.any { !isFinished(it.state) && shouldHandlerWaitForOther(handler, it) }

private fun shouldBeCancelledByFinishedHandler(handler: GestureHandler<*>) = gestureHandlers.any {
shouldHandlerWaitForOther(handler, it) && it.state == GestureHandler.STATE_END
}
private fun shouldBeCancelledByFinishedHandler(handler: GestureHandler) =
gestureHandlers.any { shouldHandlerWaitForOther(handler, it) && it.state == GestureHandler.STATE_END }

private fun shouldBeCancelledByActiveHandler(handler: GestureHandler<*>) = gestureHandlers.any {
private fun shouldBeCancelledByActiveHandler(handler: GestureHandler) = gestureHandlers.any {
handler.hasCommonPointers(it) &&
it.state == GestureHandler.STATE_ACTIVE &&
!canRunSimultaneously(handler, it) &&
handler.isDescendantOf(it)
}

private fun tryActivate(handler: GestureHandler<*>) {
private fun tryActivate(handler: GestureHandler) {
// If we are waiting for a gesture that has successfully finished, we should cancel handler
if (shouldBeCancelledByFinishedHandler(handler) || shouldBeCancelledByActiveHandler(handler)) {
handler.cancel()
Expand Down Expand Up @@ -129,7 +127,7 @@ class GestureHandlerOrchestrator(
}

/*package*/
fun onHandlerStateChange(handler: GestureHandler<*>, newState: Int, prevState: Int) {
fun onHandlerStateChange(handler: GestureHandler, newState: Int, prevState: Int) {
handlingChangeSemaphore += 1
if (isFinished(newState)) {
// We have to loop through copy in order to avoid modifying collection
Expand Down Expand Up @@ -193,7 +191,7 @@ class GestureHandlerOrchestrator(
scheduleFinishedHandlersCleanup()
}

private fun makeActive(handler: GestureHandler<*>) {
private fun makeActive(handler: GestureHandler) {
val currentState = handler.state
with(handler) {
isAwaiting = false
Expand Down Expand Up @@ -270,7 +268,7 @@ class GestureHandlerOrchestrator(
}
}

private fun deliverEventToGestureHandler(handler: GestureHandler<*>, sourceEvent: MotionEvent) {
private fun deliverEventToGestureHandler(handler: GestureHandler, sourceEvent: MotionEvent) {
if (!isViewAttachedUnderWrapper(handler.view)) {
handler.cancel()
return
Expand Down Expand Up @@ -424,7 +422,7 @@ class GestureHandlerOrchestrator(
return point
}

private fun addAwaitingHandler(handler: GestureHandler<*>) {
private fun addAwaitingHandler(handler: GestureHandler) {
if (awaitingHandlers.contains(handler)) {
return
}
Expand All @@ -438,7 +436,7 @@ class GestureHandlerOrchestrator(
}
}

private fun recordHandlerIfNotPresent(handler: GestureHandler<*>, view: View) {
private fun recordHandlerIfNotPresent(handler: GestureHandler, view: View) {
if (gestureHandlers.contains(handler)) {
return
}
Expand Down Expand Up @@ -497,7 +495,7 @@ class GestureHandlerOrchestrator(
// There's only one exception - RootViewGestureHandler. TalkBack uses hover events,
// so we need to pass them into RootViewGestureHandler, otherwise press and hold
// gesture stops working correctly (see https://github.com/software-mansion/react-native-gesture-handler/issues/3407)
private fun shouldHandlerSkipHoverEvents(handler: GestureHandler<*>, action: Int): Boolean {
private fun shouldHandlerSkipHoverEvents(handler: GestureHandler, action: Int): Boolean {
val shouldSkipHoverEvents =
handler !is HoverGestureHandler &&
handler !is RNGestureHandlerRootHelper.RootViewGestureHandler
Expand Down Expand Up @@ -672,7 +670,7 @@ class GestureHandlerOrchestrator(
private val matrixTransformCoords = FloatArray(2)
private val inverseMatrix = Matrix()
private val tempCoords = FloatArray(2)
private val handlersComparator = Comparator<GestureHandler<*>> { a, b ->
private val handlersComparator = Comparator<GestureHandler> { a, b ->
return@Comparator if (a.isActive && b.isActive || a.isAwaiting && b.isAwaiting) {
// both A and B are either active or awaiting activation, in which case we prefer one that
// has activated (or turned into "awaiting" state) earlier
Expand Down Expand Up @@ -726,17 +724,16 @@ class GestureHandlerOrchestrator(
private fun isTransformedTouchPointInView(x: Float, y: Float, child: View) =
x in 0f..child.width.toFloat() && y in 0f..child.height.toFloat()

private fun shouldHandlerWaitForOther(handler: GestureHandler<*>, other: GestureHandler<*>): Boolean =
handler !== other &&
(
handler.shouldWaitForHandlerFailure(other) ||
other.shouldRequireToWaitForFailure(handler)
)
private fun shouldHandlerWaitForOther(handler: GestureHandler, other: GestureHandler): Boolean =
handler !== other && (
handler.shouldWaitForHandlerFailure(other) ||
other.shouldRequireToWaitForFailure(handler)
)

private fun canRunSimultaneously(a: GestureHandler<*>, b: GestureHandler<*>) =
private fun canRunSimultaneously(a: GestureHandler, b: GestureHandler) =
a === b || a.shouldRecognizeSimultaneously(b) || b.shouldRecognizeSimultaneously(a)

private fun shouldHandlerBeCancelledBy(handler: GestureHandler<*>, other: GestureHandler<*>): Boolean {
private fun shouldHandlerBeCancelledBy(handler: GestureHandler, other: GestureHandler): Boolean {
if (!handler.hasCommonPointers(other)) {
// if two handlers share no common pointer one can never trigger cancel for the other
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import android.view.View
import java.util.*

interface GestureHandlerRegistry {
fun getHandlersForView(view: View): ArrayList<GestureHandler<*>>?
fun getHandlersForView(view: View): ArrayList<GestureHandler>?
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper
import com.swmansion.gesturehandler.react.RNViewConfigurationHelper
import com.swmansion.gesturehandler.react.eventbuilders.HoverGestureHandlerEventDataBuilder

class HoverGestureHandler : GestureHandler<HoverGestureHandler>() {
class HoverGestureHandler : GestureHandler() {
private var handler: Handler? = null
private var finishRunnable = Runnable { finish() }
var stylusData: StylusData = StylusData()
private set

private infix fun isAncestorOf(other: GestureHandler<*>): Boolean {
private infix fun isAncestorOf(other: GestureHandler): Boolean {
var current: View? = other.view

while (current != null) {
Expand Down Expand Up @@ -50,15 +50,15 @@ class HoverGestureHandler : GestureHandler<HoverGestureHandler>() {
return null
}

override fun shouldBeCancelledBy(handler: GestureHandler<*>): Boolean {
override fun shouldBeCancelledBy(handler: GestureHandler): Boolean {
if (handler is HoverGestureHandler && !(handler isAncestorOf this)) {
return isViewDisplayedOverAnother(handler.view!!, this.view!!)!!
}

return super.shouldBeCancelledBy(handler)
}

override fun shouldRequireToWaitForFailure(handler: GestureHandler<*>): Boolean {
override fun shouldRequireToWaitForFailure(handler: GestureHandler): Boolean {
if (handler is HoverGestureHandler) {
if (!(this isAncestorOf handler) && !(handler isAncestorOf this)) {
isViewDisplayedOverAnother(this.view!!, handler.view!!)?.let {
Expand All @@ -70,10 +70,8 @@ class HoverGestureHandler : GestureHandler<HoverGestureHandler>() {
return super.shouldRequireToWaitForFailure(handler)
}

override fun shouldRecognizeSimultaneously(handler: GestureHandler<*>): Boolean {
if (handler is HoverGestureHandler &&
(this isAncestorOf handler || handler isAncestorOf this)
) {
override fun shouldRecognizeSimultaneously(handler: GestureHandler): Boolean {
if (handler is HoverGestureHandler && (this isAncestorOf handler || handler isAncestorOf this)) {
return true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.PixelUtil
import com.swmansion.gesturehandler.react.eventbuilders.LongPressGestureHandlerEventDataBuilder

class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestureHandler>() {
class LongPressGestureHandler(context: Context) : GestureHandler() {
var minDurationMs = DEFAULT_MIN_DURATION_MS
val duration: Int
get() = (previousTime - startTime).toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context
import android.view.MotionEvent
import com.swmansion.gesturehandler.react.eventbuilders.ManualGestureHandlerEventDataBuilder

class ManualGestureHandler : GestureHandler<ManualGestureHandler>() {
class ManualGestureHandler : GestureHandler() {
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
if (state == STATE_UNDETERMINED) {
begin()
Expand Down
Loading