Repository navigation
Commit b886eef
authored
[Android] Check only the handlers extracted for the same pointer (#4632)
## Description
On Android, while one finger rests on an empty part of the screen, a tap
with a second finger on a `RectButton`, `Touchable` or `Pressable` does
nothing: no `onBegin`, `onPressIn` or `onPress`. The same tap with no
other finger down presses the button. The press right after the
swallowed one is lost as well.
Since #4127 the orchestrator asks a button, when it is recorded, whether
it may begin given the handlers already recorded, and the button refuses
any handler that is not simultaneous with it, not on its own view and
not a hover. The list passed is the orchestrator's whole
`gestureHandlers`, which also holds the handlers recorded by other
pointers. A resting finger keeps the root view's own handler recorded,
so the second finger's button is cancelled before it begins. With one
finger the root handler is recorded after the button, which is why the
check only broke with two.
`recordHandlerIfNotPresent` now takes the pointer being extracted and
passes only the handlers that already track it. Extraction is bottom-up
and every handler starts tracking the pointer right after it is
recorded, so those are exactly the handlers under the button on this
touch, which is what #4127 meant by "already extracted". Handlers from
other pointers no longer take part and keep being arbitrated at
activation time. Call sites without a pointer (the TalkBack and keyboard
synthetic click, manual state set from JS) pass an empty list, since
nothing is under a touch that does not exist.
Fixes #4627.
## Test plan
- Repro from the issue in the empty example: with a finger resting on a
plain view, a second finger presses `RectButton`, `Touchable` and
`Pressable`; single-finger presses afterwards still work. Verified on a
Pixel 9 Pro emulator (API 36) and a Galaxy S24.
- `argent flow run nested-touchables-test` (the #4127 behaviour) passes.
<details>
<summary>Test code:</summary>
```tsx
// Repro for #4627: on Android, a second finger's tap on a button does nothing
// while another finger rests on the screen. The resting finger records the
// root view's handler; the button's `shouldBeginWithRecordedHandlers` (#4127)
// then refuses it and the button is cancelled before it begins.
import React, { useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {
GestureDetector,
GestureHandlerRootView,
Pressable,
RectButton,
Touchable,
useManualGesture,
} from 'react-native-gesture-handler';
// 1. Rest one finger on the grey area.
// 2. Tap each row with a second finger: expected a log + counter bump, got nothing.
// 3. Lift both, tap each row with one finger: it works.
export default function SecondFingerBesideRestingFinger() {
const [counts, setCounts] = useState({
rect: 0,
touchable: 0,
pressable: 0,
tap: 0,
});
const bump = (key: keyof typeof counts) => {
console.log(`[${key}] press`);
setCounts((c) => ({ ...c, [key]: c[key] + 1 }));
};
// Control: a manual gesture is not a button, so #4127's check does not apply
// to it. It counts touch-downs, so it ignores how many pointers are on screen.
const tap = useManualGesture({
runOnJS: true,
onTouchesDown: (e) => {
console.log('[control] touchesDown', e.numberOfTouches);
bump('tap');
},
onTouchesUp: (e) => console.log('[control] touchesUp', e.numberOfTouches),
});
return (
<GestureHandlerRootView style={styles.container}>
<View testID="resting-area" style={styles.restingArea}>
<Text>rest a finger here</Text>
</View>
<RectButton
testID="rect-button"
style={styles.button}
onBegin={() => console.log('[rect] onBegin')}
onFinalize={() => console.log('[rect] onFinalize')}
onTouchesDown={(e) =>
console.log('[rect] touchesDown', e.numberOfTouches)
}
onTouchesUp={(e) => console.log('[rect] touchesUp', e.numberOfTouches)}
onTouchesCancel={() => console.log('[rect] touchesCancel')}
onPress={() => bump('rect')}>
<Text>RectButton: {counts.rect}</Text>
</RectButton>
<Touchable
testID="touchable"
style={styles.button}
onPressIn={() => console.log('[touchable] onPressIn')}
onPress={() => bump('touchable')}>
<Text>Touchable: {counts.touchable}</Text>
</Touchable>
<Pressable
testID="pressable"
style={styles.button}
onPressIn={() => console.log('[pressable] onPressIn')}
onPress={() => bump('pressable')}>
<Text>Pressable: {counts.pressable}</Text>
</Pressable>
<GestureDetector gesture={tap}>
<View testID="tap-detector" style={[styles.button, styles.control]}>
<Text>Manual gesture (control): {counts.tap}</Text>
</View>
</GestureDetector>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
gap: 24,
padding: 16,
},
restingArea: {
width: 240,
height: 80,
backgroundColor: '#e0e0e0',
alignItems: 'center',
justifyContent: 'center',
},
button: {
width: 240,
height: 48,
backgroundColor: '#b58df1',
alignItems: 'center',
justifyContent: 'center',
},
control: {
backgroundColor: '#8fd3b5',
},
});
```
</details>1 parent 4e18d5f commit b886eef
2 files changed
Lines changed: 8 additions & 6 deletions
File tree
- packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
258 | 258 | | |
259 | 259 | | |
260 | 260 | | |
261 | | - | |
| 261 | + | |
262 | 262 | | |
263 | 263 | | |
264 | 264 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
539 | 539 | | |
540 | 540 | | |
541 | 541 | | |
542 | | - | |
| 542 | + | |
543 | 543 | | |
544 | 544 | | |
545 | 545 | | |
| |||
549 | 549 | | |
550 | 550 | | |
551 | 551 | | |
552 | | - | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
553 | 555 | | |
554 | 556 | | |
555 | 557 | | |
| |||
595 | 597 | | |
596 | 598 | | |
597 | 599 | | |
598 | | - | |
| 600 | + | |
599 | 601 | | |
600 | 602 | | |
601 | 603 | | |
| |||
660 | 662 | | |
661 | 663 | | |
662 | 664 | | |
663 | | - | |
| 665 | + | |
664 | 666 | | |
665 | 667 | | |
666 | 668 | | |
| |||
697 | 699 | | |
698 | 700 | | |
699 | 701 | | |
700 | | - | |
| 702 | + | |
701 | 703 | | |
702 | 704 | | |
703 | 705 | | |
| |||
0 commit comments