Skip to content

Commit cd01505

Browse files
[Android] Press buttons from hardware keys and accessibility services
- ButtonViewGroup records a click's source while View's own onKeyDown, onKeyUp or performAccessibilityAction runs, admits that press while no child button is pressed, activates its native gesture as KEY or OTHER, and calls super.performClick(), which plays the click sound and sends TYPE_VIEW_CLICKED; receivedKeyEvent and the touch-exploration branch are gone. - An activation no pointer drives reports no pointer and numberOfPointers 0, at the view's visible center mapped through every view's transform, the root view's included; makeActive asks React Native to cancel its touch only for an activation reporting a pointer, and activateNativeHandlersForView skips a disabled handler and takes POINTER_TYPE_OTHER by default. - LegacyPressable and StatefulPressable take a native gesture reporting no pointer around their touch-based state machine, at the position it reports, while a tvOS focus press keeps the layout center; NativeViewGestureHandlerPayload declares the coordinates #4302 emits. - isKeyboardDismissingTap takes the press's pointer count, and StatefulPressable's long press clears a touch's keyboard verdict as it finalizes. - The Keyboard activation example with keyboard-activation-test.yaml and keyboard-touchable-test.yaml, pressableClickWithoutTouch.test.tsx and the api_v3 cases.
1 parent 0c12342 commit cd01505

18 files changed

Lines changed: 1363 additions & 75 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Android only: a hardware Enter presses each kind of focused gesture-handler
2+
# control once, as a key with no pointer, where a tap would land. Tab moves
3+
# focus in view order, so after the taps and the console clear the counts below
4+
# reach the inner default Pressable, then each control after it.
5+
steps:
6+
- echo: Launching Expo Example
7+
- launch: com.example.ExpoExample
8+
- await: { visible: { text: "Empty Example" }, timeout: 15000 }
9+
- when: { platform: android }
10+
steps:
11+
- echo: "On the Home list, search for the keyboard activation example"
12+
- type: { text: "keyboard activation", into: { id: "search-examples" } }
13+
- await: { visible: { text: "Keyboard activation" } }
14+
- echo: "Tap the example to open it"
15+
- tap: { text: "Keyboard activation" }
16+
- await: { visible: { id: "scaled-touchable" } }
17+
- await: { idle: true }
18+
19+
- echo: "Clear console"
20+
- await: { visible: { id: "open-console-button" } }
21+
- tap: { id: "open-console-button" }
22+
- await: { visible: { id: "clear-console-button" } }
23+
- tap: { id: "clear-console-button" }
24+
- tap: { id: "close-console-button" }
25+
- await: { hidden: { id: "close-console-button" } }
26+
- await: { idle: true }
27+
28+
- echo: "Tap the inner state-machine and legacy Pressables, the RectButton and the scaled Touchable"
29+
- tap: { id: "stateful-inner" }
30+
- tap: { id: "legacy-inner" }
31+
- tap: { id: "rect-button" }
32+
- tap: { id: "scaled-touchable" }
33+
- await: { idle: true }
34+
35+
- echo: "Each tap presses only its own control"
36+
- tap: { id: "open-console-button" }
37+
- await: { idle: true }
38+
- await: { visible: { id: "close-console-button" } }
39+
- assert: { visible: { text: { matches: '^3\. \[stateful inner\] onPress$' } } }
40+
- assert: { visible: { text: { matches: '^6\. \[legacy inner\] onPress$' } } }
41+
- assert: { visible: { text: { matches: '^7\. \[rect button\] onBegin TOUCH with 1 pointers$' } } }
42+
- assert: { visible: { text: { matches: '^9\. \[scaled\] tap$' } } }
43+
- assert: { hidden: { text: "outer]" } }
44+
45+
- echo: "Clear console"
46+
- tap: { id: "clear-console-button" }
47+
- tap: { id: "close-console-button" }
48+
- await: { hidden: { id: "close-console-button" } }
49+
- await: { idle: true }
50+
51+
- echo: "Tab to the inner default Pressable, which no pointer has touched, and press Enter"
52+
- tool: keyboard
53+
args: { key: tab }
54+
- tool: keyboard
55+
args: { key: tab }
56+
- tool: keyboard
57+
args: { key: enter }
58+
- echo: "Tab to the state-machine Pressable whose inner was tapped, and press Enter"
59+
- tool: keyboard
60+
args: { key: tab }
61+
- tool: keyboard
62+
args: { key: enter }
63+
- echo: "Tab past its inner to the legacy Pressable whose inner was tapped, and press Enter"
64+
- tool: keyboard
65+
args: { key: tab }
66+
- tool: keyboard
67+
args: { key: tab }
68+
- tool: keyboard
69+
args: { key: enter }
70+
- echo: "Each Enter presses its Pressable once, the parent of a tapped one included"
71+
- tap: { id: "open-console-button" }
72+
- await: { idle: true }
73+
- await: { visible: { id: "close-console-button" } }
74+
- assert: { visible: { text: { matches: '^10\. \[pressable inner\] onPressIn$' } } }
75+
- assert: { visible: { text: { matches: '^11\. \[pressable inner\] onPressOut$' } } }
76+
- assert: { visible: { text: { matches: '^12\. \[pressable inner\] onPress$' } } }
77+
- assert: { visible: { text: { matches: '^13\. \[stateful outer\] onPressIn$' } } }
78+
- assert: { visible: { text: { matches: '^14\. \[stateful outer\] onPressOut$' } } }
79+
- assert: { visible: { text: { matches: '^15\. \[stateful outer\] onPress$' } } }
80+
- assert: { visible: { text: { matches: '^16\. \[legacy outer\] onPressIn$' } } }
81+
- assert: { visible: { text: { matches: '^17\. \[legacy outer\] onPressOut$' } } }
82+
- assert: { visible: { text: { matches: '^18\. \[legacy outer\] onPress$' } } }
83+
- assert: { hidden: { text: "[pressable outer]" } }
84+
- assert: { hidden: { text: { matches: '^19\. ' } } }
85+
86+
- echo: "Clear console"
87+
- tap: { id: "clear-console-button" }
88+
- tap: { id: "close-console-button" }
89+
- await: { hidden: { id: "close-console-button" } }
90+
- await: { idle: true }
91+
92+
- echo: "Tab to the RectButton, the seventh control, and press Enter"
93+
- tool: keyboard
94+
args: { key: tab }
95+
- tool: keyboard
96+
args: { key: tab }
97+
- tool: keyboard
98+
args: { key: tab }
99+
- tool: keyboard
100+
args: { key: tab }
101+
- tool: keyboard
102+
args: { key: tab }
103+
- tool: keyboard
104+
args: { key: tab }
105+
- tool: keyboard
106+
args: { key: tab }
107+
- tool: keyboard
108+
args: { key: enter }
109+
- echo: "Tab to the scaled Touchable and press Enter"
110+
- tool: keyboard
111+
args: { key: tab }
112+
- tool: keyboard
113+
args: { key: enter }
114+
115+
- echo: "Enter reports a key with no pointer, and lands where the tap did"
116+
- tap: { id: "open-console-button" }
117+
- await: { idle: true }
118+
- await: { visible: { id: "close-console-button" } }
119+
- assert: { visible: { text: { matches: '^19\. \[rect button\] onBegin KEY with 0 pointers$' } } }
120+
- assert: { visible: { text: { matches: '^20\. \[rect button\] onPress$' } } }
121+
- assert: { visible: { text: { matches: '^21\. \[scaled\] key press lands on the tap$' } } }
122+
- assert: { hidden: { text: { matches: '^22\. ' } } }
123+
124+
- echo: "Clear console"
125+
- tap: { id: "clear-console-button" }
126+
- tap: { id: "close-console-button" }
127+
- await: { hidden: { id: "close-console-button" } }
128+
- await: { idle: true }
129+
130+
- echo: "Tap the input inside the ScrollView, so the soft keyboard is up for it, then Tab to the Pressable beside it and press Enter"
131+
- tap: { id: "typing-input" }
132+
- await: { idle: true }
133+
- tool: keyboard
134+
args: { key: tab }
135+
- tool: keyboard
136+
args: { key: enter }
137+
138+
- echo: "A key press is no tap dismissing the keyboard, so it presses the Pressable once"
139+
- tap: { id: "open-console-button" }
140+
- await: { idle: true }
141+
- await: { visible: { id: "close-console-button" } }
142+
- assert: { visible: { text: { matches: '^22\. \[typing pressable\] onPressIn$' } } }
143+
- assert: { visible: { text: { matches: '^23\. \[typing pressable\] onPressOut$' } } }
144+
- assert: { visible: { text: { matches: '^24\. \[typing pressable\] onPress$' } } }
145+
- assert: { hidden: { text: { matches: '^25\. ' } } }
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Android only: a hardware Enter or Space presses the focused touchable. Tab
2+
# moves focus in view order, so the second Tab reaches the level 4 touchable.
3+
steps:
4+
- echo: Launching Expo Example
5+
- launch: com.example.ExpoExample
6+
- await: { visible: { text: "Empty Example" }, timeout: 15000 }
7+
- when: { platform: android }
8+
steps:
9+
- echo: "On the Home list, search for the nested touchables example"
10+
- type: { text: "nested touchables", into: { id: "search-examples" } }
11+
- await: { visible: { text: "Nested touchables" } }
12+
- echo: "Tap the example to open it"
13+
- tap: { text: "Nested touchables" }
14+
- await: { visible: { id: "level-4" } }
15+
- await: { idle: true }
16+
17+
- echo: "Clear console"
18+
- await: { visible: { id: "open-console-button" } }
19+
- tap: { id: "open-console-button" }
20+
- await: { visible: { id: "clear-console-button" } }
21+
- tap: { id: "clear-console-button" }
22+
- tap: { id: "close-console-button" }
23+
- await: { hidden: { id: "close-console-button" } }
24+
- await: { idle: true }
25+
26+
- echo: "Tab twice to focus the level 4 touchable, then press Enter and Space"
27+
- tool: keyboard
28+
args: { key: tab }
29+
- tool: keyboard
30+
args: { key: tab }
31+
- tool: keyboard
32+
args: { key: enter }
33+
- tool: keyboard
34+
args: { key: space }
35+
36+
- echo: "Each key presses level 4 once and no other level logs"
37+
- tap: { id: "open-console-button" }
38+
- await: { idle: true }
39+
- await: { visible: { id: "close-console-button" } }
40+
- assert: { visible: { text: { matches: '^1\. L4 onPressIn$' } } }
41+
- assert: { visible: { text: { matches: '^2\. L4 onPressOut$' } } }
42+
- assert: { visible: { text: { matches: '^3\. L4 onPress$' } } }
43+
- assert: { visible: { text: { matches: '^4\. L4 onPressIn$' } } }
44+
- assert: { visible: { text: { matches: '^5\. L4 onPressOut$' } } }
45+
- assert: { visible: { text: { matches: '^6\. L4 onPress$' } } }
46+
- assert: { hidden: { text: "L1 on" } }
47+
- assert: { hidden: { text: "L2 on" } }
48+
- assert: { hidden: { text: "L3 on" } }
49+
- assert: { hidden: { text: "7. L" } }

‎apps/common-app/src/new_api/index.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import PinchExample from './simple/pinch';
3535
import RotationExample from './simple/rotation';
3636
import TapExample from './simple/tap';
3737
import ButtonChildOrderExample from './tests/buttonChildOrder';
38+
import KeyboardActivationExample from './tests/keyboardActivation';
3839
import KeyboardShouldPersistTapsExample from './tests/keyboardShouldPersistTaps';
3940
import NativeInterceptCancellationExample from './tests/nativeInterceptCancellation';
4041
import NestedPressablesExample from './tests/nestedPressables';
@@ -133,6 +134,7 @@ export const NEW_EXAMPLES: ExamplesSection[] = [
133134
{ name: 'Nested pressables', component: NestedPressablesExample },
134135
{ name: 'Nested touchables', component: NestedTouchablesExample },
135136
{ name: 'Button child order', component: ButtonChildOrderExample },
137+
{ name: 'Keyboard activation', component: KeyboardActivationExample },
136138
{
137139
name: 'Native intercept cancellation',
138140
component: NativeInterceptCancellationExample,

0 commit comments

Comments
 (0)