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 @@ -172,8 +172,12 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
windowOffset[0] = 0
windowOffset[1] = 0
}

onPrepare()
}

protected open fun onPrepare() {}

private fun getWindow(context: Context?): Window? {
if (context == null) return null
if (context is Activity) return context.window
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import android.os.SystemClock
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import com.swmansion.gesturehandler.react.RNGestureHandlerButtonViewManager

class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
private var shouldActivateOnStart = false
private var disallowInterruption = false

private var hook: NativeViewGestureHandlerHook = defaultHook

init {
setShouldCancelWhenOutside(true)
}
Expand All @@ -34,13 +35,17 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
}

override fun shouldRecognizeSimultaneously(handler: GestureHandler<*>): Boolean {
// if the gesture is marked by user as simultaneous with other or the hook return true
if (super.shouldRecognizeSimultaneously(handler) || hook.shouldRecognizeSimultaneously(handler)) {
return true
}

if (handler is NativeViewGestureHandler) {
// Special case when the peer handler is also an instance of NativeViewGestureHandler:
// For the `disallowInterruption` to work correctly we need to check the property when
// accessed as a peer, because simultaneous recognizers can be set on either side of the
// connection.
val nativeWrapper = handler
if (nativeWrapper.state == STATE_ACTIVE && nativeWrapper.disallowInterruption) {
if (handler.state == STATE_ACTIVE && handler.disallowInterruption) {
// other handler is active and it disallows interruption, we don't want to get into its way
return false
}
Expand All @@ -52,27 +57,17 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
// as it means the other handler has turned active and returning `true` would prevent it from
// interrupting the current handler
false
} else state == STATE_ACTIVE && canBeInterrupted
} else state == STATE_ACTIVE && canBeInterrupted && (!hook.shouldCancelRootViewGestureHandlerIfNecessary() || handler.tag > 0)
// otherwise we can only return `true` if already in an active state
}

override fun shouldBeCancelledBy(handler: GestureHandler<*>): Boolean {
return !disallowInterruption
}

private fun canStart(): Boolean {
val view = view
if (view is StateChangeHook) {
return view.canStart()
}

return true
}

private fun afterGestureEnd() {
val view = view
if (view is StateChangeHook) {
view.afterGestureEnd()
override fun onPrepare() {
when (val view = view) {
is NativeViewGestureHandlerHook -> this.hook = view
}
}

Expand All @@ -84,7 +79,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
activate()
}
end()
afterGestureEnd()
hook.afterGestureEnd(event)
} else if (state == STATE_UNDETERMINED || state == STATE_BEGAN) {
when {
shouldActivateOnStart -> {
Expand All @@ -96,8 +91,11 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
view.onTouchEvent(event)
activate()
}
hook.wantsToHandleEventBeforeActivation() -> {
hook.handleEventBeforeActivation(event)
}
state != STATE_BEGAN -> {
if (canStart()) {
if (hook.canBegin()) {
begin()
} else {
cancel()
Expand All @@ -117,13 +115,55 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
view!!.onTouchEvent(event)
}

override fun onReset() {
this.hook = defaultHook
}

companion object {
private fun tryIntercept(view: View, event: MotionEvent) =
view is ViewGroup && view.onInterceptTouchEvent(event)

private val defaultHook = object : NativeViewGestureHandlerHook {}
}

interface StateChangeHook {
fun canStart(): Boolean
fun afterGestureEnd()
interface NativeViewGestureHandlerHook {
/**
* Called when gesture is in the UNDETERMINED state, shouldActivateOnStart is set to false,
* and both tryIntercept and wantsToHandleEventBeforeActivation returned false.
*
* @return Boolean value signalling whether the handler can transition to the BEGAN state. If false
* the gesture will be cancelled.
*/
fun canBegin() = true

/**
* Called after the gesture transitions to the END state.
*/
fun afterGestureEnd(event: MotionEvent) = Unit

/**
* @return Boolean value signalling whether the gesture can be recognized simultaneously with
* other (handler). Returning false doesn't necessarily prevent it from happening.
*/
fun shouldRecognizeSimultaneously(handler: GestureHandler<*>) = false

/**
* shouldActivateOnStart and tryIntercept have priority over this method
*
* @return Boolean value signalling if the hook wants to handle events passed to the handler
* before it activates (after that the events are passed to the underlying view).
*/
fun wantsToHandleEventBeforeActivation() = false

/**
* Will be called with events if wantsToHandleEventBeforeActivation returns true.
*/
fun handleEventBeforeActivation(event: MotionEvent) = Unit

/**
* @return Boolean value indicating whether the RootViewGestureHandler should be cancelled
* by this one.
*/
fun shouldCancelRootViewGestureHandlerIfNecessary() = false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
}

class ButtonViewGroup(context: Context?) : ViewGroup(context),
NativeViewGestureHandler.StateChangeHook {
NativeViewGestureHandler.NativeViewGestureHandlerHook {
// Using object because of handling null representing no value set.
var rippleColor: Int? = null
set(color) = withBackgroundUpdate {
Expand Down Expand Up @@ -265,15 +265,15 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
}
}

override fun canStart(): Boolean {
override fun canBegin(): Boolean {
val isResponder = tryGrabbingResponder()
if (isResponder) {
isTouched = true
}
return isResponder
}

override fun afterGestureEnd() {
override fun afterGestureEnd(event: MotionEvent) {
tryFreeingResponder()
isTouched = false
}
Expand Down