com.facebook.react.touch.JSResponderHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of react-native Show documentation
Show all versions of react-native Show documentation
A framework for building native apps with React
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.touch;
import javax.annotation.Nullable;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.ViewParent;
/**
* This class coordinates JSResponder commands for {@link UIManagerModule}. It should be set as
* OnInterceptTouchEventListener for all newly created native views that implements
* {@link ReactInterceptingViewGroup} and thanks to the information whether JSResponder is set
* and to which view it will correctly coordinate the return values of
* {@link OnInterceptTouchEventListener} such that touch events will be dispatched to the view
* selected by JS gesture recognizer.
*
* Single {@link CatalystInstance} should reuse same instance of this class.
*/
public class JSResponderHandler implements OnInterceptTouchEventListener {
private static final int JS_RESPONDER_UNSET = -1;
private volatile int mCurrentJSResponder = JS_RESPONDER_UNSET;
// We're holding on to the ViewParent that blocked native responders so that we can clear it
// when we change or clear the current JS responder.
private @Nullable ViewParent mViewParentBlockingNativeResponder;
public void setJSResponder(int tag, @Nullable ViewParent viewParentBlockingNativeResponder) {
mCurrentJSResponder = tag;
// We need to unblock the native responder first, otherwise we can get in a bad state: a
// ViewParent sets requestDisallowInterceptTouchEvent to true, which sets this setting to true
// to all of its ancestors. Now, if one of its ancestors sets requestDisallowInterceptTouchEvent
// to false, it unsets the setting for itself and all of its ancestors, which means that they
// can intercept events again.
maybeUnblockNativeResponder();
if (viewParentBlockingNativeResponder != null) {
viewParentBlockingNativeResponder.requestDisallowInterceptTouchEvent(true);
mViewParentBlockingNativeResponder = viewParentBlockingNativeResponder;
}
}
public void clearJSResponder() {
mCurrentJSResponder = JS_RESPONDER_UNSET;
maybeUnblockNativeResponder();
}
private void maybeUnblockNativeResponder() {
if (mViewParentBlockingNativeResponder != null) {
mViewParentBlockingNativeResponder.requestDisallowInterceptTouchEvent(false);
mViewParentBlockingNativeResponder = null;
}
}
@Override
public boolean onInterceptTouchEvent(ViewGroup v, MotionEvent event) {
int currentJSResponder = mCurrentJSResponder;
if (currentJSResponder != JS_RESPONDER_UNSET && event.getAction() != MotionEvent.ACTION_UP) {
// Don't intercept ACTION_UP events. If we return true here than UP event will not be
// delivered. That is because intercepted touch events are converted into CANCEL events
// and make all further events to be delivered to the view that intercepted the event.
// Therefore since "UP" event is the last event in a gesture, we should just let it reach the
// original target that is a child view of {@param v}.
// http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)
return v.getId() == currentJSResponder;
}
return false;
}
}