diff --git a/android/src/react-versioned/latest/com/swmansion/rnscreens/bottomsheet/RNSModalRootView.kt b/android/src/react-versioned/latest/com/swmansion/rnscreens/bottomsheet/RNSModalRootView.kt new file mode 100644 index 0000000000..691284df59 --- /dev/null +++ b/android/src/react-versioned/latest/com/swmansion/rnscreens/bottomsheet/RNSModalRootView.kt @@ -0,0 +1,99 @@ +import android.annotation.SuppressLint +import android.view.MotionEvent +import android.view.View +import com.facebook.react.bridge.ReactContext +import com.facebook.react.config.ReactFeatureFlags +import com.facebook.react.uimanager.JSPointerDispatcher +import com.facebook.react.uimanager.JSTouchDispatcher +import com.facebook.react.uimanager.RootView +import com.facebook.react.uimanager.events.EventDispatcher +import com.facebook.react.views.view.ReactViewGroup + +@SuppressLint("ViewConstructor") +class RNSModalRootView( + val reactContext: ReactContext?, + private val eventDispatcher: EventDispatcher +) : ReactViewGroup(reactContext), RootView { + private val jsTouchDispatcher: JSTouchDispatcher = JSTouchDispatcher(this) + private var jsPointerDispatcher: JSPointerDispatcher? = null + + init { + // Can we safely use ReactFeatureFlags? + if (ReactFeatureFlags.dispatchPointerEvents) { + jsPointerDispatcher = JSPointerDispatcher(this) + } + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + if (changed) { + // This view is used right now only in ScreenModalFragment, where it is injected + // to view hierarchy as a parent of a Screen. + if (childCount > 1) { + val grabberWidthSpec = MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY) + val grabberHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) + + val grabber = getChildAt(0) + grabber.measure(grabberWidthSpec, grabberHeightSpec) + grabber.layout(l, t, r, grabber.measuredHeight) + getChildAt(childCount - 1).layout(l, t + grabber.measuredHeight, r, b) + } else { + getChildAt(childCount - 1).layout(l, t, r, b) + } + } + } + + override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + jsTouchDispatcher.handleTouchEvent(event, eventDispatcher) + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true) + return super.onInterceptTouchEvent(event) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + jsTouchDispatcher.handleTouchEvent(event, eventDispatcher) + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false) + super.onTouchEvent(event) + return true + } + + override fun onInterceptHoverEvent(event: MotionEvent): Boolean { + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true) + // This is how DialogRootViewGroup implements this, it might be a copy-paste mistake + // on their side. + return super.onHoverEvent(event) + } + + override fun onHoverEvent(event: MotionEvent): Boolean { + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false) + return super.onHoverEvent(event) + } + + override fun onChildStartedNativeGesture(view: View, event: MotionEvent) { + jsTouchDispatcher.onChildStartedNativeGesture(event, eventDispatcher) + jsPointerDispatcher?.onChildStartedNativeGesture(view, event, eventDispatcher) + } + + @Deprecated("Deprecated by React Native") + override fun onChildStartedNativeGesture(event: MotionEvent) { + throw IllegalStateException("Deprecated onChildStartedNativeGesture was called") + } + + override fun onChildEndedNativeGesture(view: View, event: MotionEvent) { + jsTouchDispatcher.onChildEndedNativeGesture(event, eventDispatcher) + jsPointerDispatcher?.onChildEndedNativeGesture() + } + + override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) { + // We do not pass through request of our child up the view hierarchy, as we + // need to keep receiving events. + } + + override fun handleException(throwable: Throwable?) { + // TODO: I need ThemedReactContext here. + // TODO: Determine where it is initially created & verify its lifecycle +// reactContext?.reactApplicationContext?.handleException(RuntimeException(throwable)) + } + + companion object { + val TAG = RNSModalRootView::class.simpleName + } +} diff --git a/android/src/react-versioned/pre72/com/swmansion/rnscreens/bottomsheet/RNSModalRootView.kt b/android/src/react-versioned/pre72/com/swmansion/rnscreens/bottomsheet/RNSModalRootView.kt new file mode 100644 index 0000000000..a612ac7b79 --- /dev/null +++ b/android/src/react-versioned/pre72/com/swmansion/rnscreens/bottomsheet/RNSModalRootView.kt @@ -0,0 +1,100 @@ +import android.annotation.SuppressLint +import android.view.MotionEvent +import android.view.View +import com.facebook.react.bridge.ReactContext +import com.facebook.react.config.ReactFeatureFlags +import com.facebook.react.uimanager.JSPointerDispatcher +import com.facebook.react.uimanager.JSTouchDispatcher +import com.facebook.react.uimanager.RootView +import com.facebook.react.uimanager.events.EventDispatcher +import com.facebook.react.views.view.ReactViewGroup + +@SuppressLint("ViewConstructor") +class RNSModalRootView( + val reactContext: ReactContext?, + private val eventDispatcher: EventDispatcher +) : ReactViewGroup(reactContext), RootView { + private val jsTouchDispatcher: JSTouchDispatcher = JSTouchDispatcher(this) + private var jsPointerDispatcher: JSPointerDispatcher? = null + + init { + // Can we safely use ReactFeatureFlags? + if (ReactFeatureFlags.dispatchPointerEvents) { + jsPointerDispatcher = JSPointerDispatcher(this) + } + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + if (changed) { + // This view is used right now only in ScreenModalFragment, where it is injected + // to view hierarchy as a parent of a Screen. + if (childCount > 1) { + val grabberWidthSpec = MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY) + val grabberHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) + + val grabber = getChildAt(0) + grabber.measure(grabberWidthSpec, grabberHeightSpec) + grabber.layout(l, t, r, grabber.measuredHeight) + getChildAt(childCount - 1).layout(l, t + grabber.measuredHeight, r, b) + } else { + getChildAt(childCount - 1).layout(l, t, r, b) + } + } + } + + override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + jsTouchDispatcher.handleTouchEvent(event, eventDispatcher) + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher) + return super.onInterceptTouchEvent(event) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + jsTouchDispatcher.handleTouchEvent(event, eventDispatcher) + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher) + super.onTouchEvent(event) + return true; + } + + override fun onInterceptHoverEvent(event: MotionEvent): Boolean { + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher) + // This is how DialogRootViewGroup implements this, it might be a copy-paste mistake + // on their side. + return super.onHoverEvent(event) + } + + override fun onHoverEvent(event: MotionEvent): Boolean { + jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher) + return super.onHoverEvent(event) + } + + + override fun onChildStartedNativeGesture(view: View, event: MotionEvent) { + jsTouchDispatcher.onChildStartedNativeGesture(event, eventDispatcher) + jsPointerDispatcher?.onChildStartedNativeGesture(view, event, eventDispatcher) + } + + @Deprecated("Deprecated by React Native") + override fun onChildStartedNativeGesture(event: MotionEvent) { + throw IllegalStateException("Deprecated onChildStartedNativeGesture was called") + } + + override fun onChildEndedNativeGesture(view: View, event: MotionEvent) { + jsTouchDispatcher.onChildEndedNativeGesture(event, eventDispatcher) + jsPointerDispatcher?.onChildEndedNativeGesture() + } + + override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) { + // We do not pass through request of our child up the view hierarchy, as we + // need to keep receiving events. + } + + override fun handleException(throwable: Throwable?) { + // TODO: I need ThemedReactContext here. + // TODO: Determine where it is initially created & verify its lifecycle +// reactContext?.reactApplicationContext?.handleException(RuntimeException(throwable)) + } + + companion object { + val TAG = RNSModalRootView::class.simpleName + } +}