react-native icon indicating copy to clipboard operation
react-native copied to clipboard

In Android touchMove is cancelled when intercepted by native gesture from child

Open AndriusZal opened this issue 9 months ago • 2 comments

Description

Hey! I'm not sure if you can call this a bug, but I might be missing some of the functionality in touch event handling. Currently, on Android touchMove event seems to be cancelled when native gesture is recognized and intercepted in child component. While going through your source code, I have noticed that it is done intentionally in JSTouchDispatcher.java:

public void onChildStartedNativeGesture(
      MotionEvent androidEvent, EventDispatcher eventDispatcher) {
    if (mChildIsHandlingNativeGesture) {
      // This means we previously had another child start handling this native gesture and now a
      // different native parent of that child has decided to intercept the touch stream and handle
      // the gesture itself. Example where this can happen: HorizontalScrollView in a ScrollView.
      return;
    }

    dispatchCancelEvent(androidEvent, eventDispatcher);
    mChildIsHandlingNativeGesture = true;
    mTargetTag = -1;
  }

I would still like to receive touchMove and specifically touchEnd events when the gesture starts and ends. However, now I would only get a few touchMove events and touchCancelled. It doesn't seem to be difficult to implement, but maybe there is a reason why this approach is chosen?

Steps to reproduce

  1. Initialize the app simply npx react-native@latest init AwesomeProject22
  2. Add a simple onTouchMove to the top view onTouchMove={()=>console.log("moving")}
  3. Run the app with npx react-native run-android
  4. Try to trigger touch move event. When moving to the sides, everything seems okay. When you start dragging to the top or bottom, only a few touchMove events are triggered and then cancelled until the next touchStart event.

React Native Version

0.74.1

Affected Platforms

Runtime - Android

Output of npx react-native info

info Fetching system and libraries information...
(node:2310) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
System:
  OS: macOS 14.4.1
  CPU: (12) arm64 Apple M2 Pro
  Memory: 118.08 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.1.0
    path: ~/.nvm/versions/node/v22.1.0/bin/node
  Yarn:
    version: 3.6.4
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.3.0
    path: ~/.nvm/versions/node/v22.1.0/bin/npm
  Watchman:
    version: 2024.04.29.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.14.3
    path: /Users/andrius.zaleckis/.rvm/gems/ruby-3.2.2/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.4
      - iOS 17.4
      - macOS 14.4
      - tvOS 17.4
      - visionOS 1.1
      - watchOS 10.4
  Android SDK:
    API Levels:
      - "34"
    Build Tools:
      - 34.0.0
      - 35.0.0
    System Images:
      - android-34 | Google APIs ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2023.3 AI-233.14808.21.2331.11709847
  Xcode:
    version: 15.3/15E204a
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.11
    path: /opt/homebrew/opt/openjdk@17/bin/javac
  Ruby:
    version: 3.2.2
    path: /Users/andrius.zaleckis/.rvm/rubies/ruby-3.2.2/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.1
    wanted: 0.74.1
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Stacktrace or Logs

No crashes or failures recorded.

Reproducer

The issue does not require a reproducer.

Screenshots and Videos

No response

AndriusZal avatar May 16 '24 17:05 AndriusZal

I don't think a reproducer is necessary in this example, but I can create it on further request.

AndriusZal avatar May 16 '24 17:05 AndriusZal

I don't think a reproducer is necessary in this example, but I can create it on further request.

Please do. It will save us time when we get to investigating this issue

cortinico avatar May 20 '24 10:05 cortinico

Have added a reproducer: https://github.com/AndriusZal/44594-Reproducer

Within the Metro you will notice that when you touch move, the log will print a message with a counter. This counter resets when touch ends. The issue occurs when dragging is recognized by native recognizer within scroll view and touch move event is replaced with dragging event and scroll is happening. Then the touch is cancelled and we are not getting the touch end event. We can trigger touch cancelled event, but I still need to see all touch move events which are happening during drag.

I don't mind to providing the fix myself, I just might need some backstory why this approach is chosen.

AndriusZal avatar May 20 '24 17:05 AndriusZal

My comment maybe unrelated to the above issue but I have a doubt regarding the usage of onChildStartedNativeGesture. Is this method supposed to be called by the framework or can this be manually called as well ? I have a use case where I have react native views on top of native views. Whenever a touch event is intercepted by ReactRootView it starts dispatching it to JavaScript but if that touch event is to be handled by one of my Native Views, I only want the Native Views to handle it. But in the correct scenario due to this implementation, it ends up being handled by both. How do I handle this gracefully ?

Robert6321 avatar May 27 '24 07:05 Robert6321

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

react-native-bot avatar Nov 24 '24 05:11 react-native-bot

This issue was closed because it has been stalled for 7 days with no activity.

react-native-bot avatar Dec 01 '24 05:12 react-native-bot