9
9
10
10
import android .view .MotionEvent ;
11
11
import com .facebook .react .bridge .Arguments ;
12
+ import com .facebook .react .bridge .ReactSoftExceptionLogger ;
12
13
import com .facebook .react .bridge .WritableArray ;
13
14
import com .facebook .react .bridge .WritableMap ;
15
+ import com .facebook .react .bridge .WritableNativeArray ;
14
16
import com .facebook .react .uimanager .PixelUtil ;
15
17
16
18
/** Class responsible for generating catalyst touch events based on android {@link MotionEvent}. */
17
19
public class TouchesHelper {
18
-
19
20
public static final String TARGET_SURFACE_KEY = "targetSurface" ;
20
21
public static final String TARGET_KEY = "target" ;
21
22
public static final String CHANGED_TOUCHES_KEY = "changedTouches" ;
@@ -28,14 +29,16 @@ public class TouchesHelper {
28
29
private static final String LOCATION_X_KEY = "locationX" ;
29
30
private static final String LOCATION_Y_KEY = "locationY" ;
30
31
32
+ private static final String TAG = "TouchesHelper" ;
33
+
31
34
/**
32
35
* Creates catalyst pointers array in format that is expected by RCTEventEmitter JS module from
33
36
* given {@param event} instance. This method use {@param reactTarget} parameter to set as a
34
37
* target view id associated with current gesture.
35
38
*/
36
- private static WritableArray createsPointersArray (TouchEvent event ) {
37
- WritableArray touches = Arguments .createArray ();
39
+ private static WritableMap [] createPointersArray (TouchEvent event ) {
38
40
MotionEvent motionEvent = event .getMotionEvent ();
41
+ WritableMap [] touches = new WritableMap [motionEvent .getPointerCount ()];
39
42
40
43
// Calculate the coordinates for the target view.
41
44
// The MotionEvent contains the X,Y of the touch in the coordinate space of the root view
@@ -63,7 +66,8 @@ private static WritableArray createsPointersArray(TouchEvent event) {
63
66
touch .putInt (TARGET_KEY , event .getViewTag ());
64
67
touch .putDouble (TIMESTAMP_KEY , event .getTimestampMs ());
65
68
touch .putDouble (POINTER_IDENTIFIER_KEY , motionEvent .getPointerId (index ));
66
- touches .pushMap (touch );
69
+
70
+ touches [index ] = touch ;
67
71
}
68
72
69
73
return touches ;
@@ -78,7 +82,8 @@ private static WritableArray createsPointersArray(TouchEvent event) {
78
82
*/
79
83
public static void sendTouchEvent (RCTEventEmitter rctEventEmitter , TouchEvent touchEvent ) {
80
84
TouchEventType type = touchEvent .getTouchEventType ();
81
- WritableArray pointers = createsPointersArray (touchEvent );
85
+
86
+ WritableArray pointers = getWritableArray (createPointersArray (touchEvent ));
82
87
MotionEvent motionEvent = touchEvent .getMotionEvent ();
83
88
84
89
// For START and END events send only index of the pointer that is associated with that event
@@ -96,4 +101,98 @@ public static void sendTouchEvent(RCTEventEmitter rctEventEmitter, TouchEvent to
96
101
97
102
rctEventEmitter .receiveTouches (TouchEventType .getJSEventName (type ), pointers , changedIndices );
98
103
}
104
+
105
+ /**
106
+ * Generate touch event data to match JS expectations. Combines logic in {@link #sendTouchEvent}
107
+ * and FabricEventEmitter to create the same data structure in a more efficient manner.
108
+ *
109
+ * <p>Touches have to be dispatched as separate events for each changed pointer to make JS process
110
+ * them correctly. To avoid allocations, we preprocess touch events in Java world and then convert
111
+ * them to native before dispatch.
112
+ *
113
+ * @param eventEmitter emitter to dispatch event to
114
+ * @param event the touch event to extract data from
115
+ * @param useDispatchV2 whether to dispatch additional data used by {@link Event#dispatchModernV2}
116
+ */
117
+ public static void sendTouchEventModern (
118
+ RCTModernEventEmitter eventEmitter , TouchEvent event , boolean useDispatchV2 ) {
119
+ TouchEventType type = event .getTouchEventType ();
120
+ MotionEvent motionEvent = event .getMotionEvent ();
121
+
122
+ if (motionEvent == null ) {
123
+ ReactSoftExceptionLogger .logSoftException (
124
+ TAG ,
125
+ new IllegalStateException (
126
+ "Cannot dispatch a TouchEvent that has no MotionEvent; the TouchEvent has been recycled" ));
127
+ return ;
128
+ }
129
+
130
+ WritableMap [] touches = createPointersArray (event );
131
+ WritableMap [] changedTouches = null ;
132
+
133
+ switch (type ) {
134
+ case START :
135
+ int newPointerIndex = motionEvent .getActionIndex ();
136
+
137
+ changedTouches = new WritableMap [] {touches [newPointerIndex ].copy ()};
138
+ break ;
139
+ case END :
140
+ int finishedPointerIndex = motionEvent .getActionIndex ();
141
+ /*
142
+ * Clear finished pointer index for compatibility with W3C touch "end" events, where the
143
+ * active touches don't include the set that has just been "ended".
144
+ */
145
+ WritableMap finishedPointer = touches [finishedPointerIndex ];
146
+ touches [finishedPointerIndex ] = null ;
147
+
148
+ changedTouches = new WritableMap [] {finishedPointer };
149
+ break ;
150
+ case MOVE :
151
+ changedTouches = new WritableMap [touches .length ];
152
+ for (int i = 0 ; i < touches .length ; i ++) {
153
+ changedTouches [i ] = touches [i ].copy ();
154
+ }
155
+ break ;
156
+ case CANCEL :
157
+ changedTouches = touches ;
158
+ touches = new WritableMap [0 ];
159
+ break ;
160
+ }
161
+
162
+ WritableArray touchesArray = getWritableArray (touches );
163
+ WritableArray changedTouchesArray = getWritableArray (/* copyObjects */ true , changedTouches );
164
+
165
+ for (WritableMap eventData : changedTouches ) {
166
+ eventData .putArray (CHANGED_TOUCHES_KEY , changedTouchesArray );
167
+ eventData .putArray (TOUCHES_KEY , touchesArray );
168
+
169
+ if (useDispatchV2 ) {
170
+ eventEmitter .receiveEvent (
171
+ event .getSurfaceId (),
172
+ event .getViewTag (),
173
+ event .getEventName (),
174
+ event .canCoalesce (),
175
+ 0 ,
176
+ eventData ,
177
+ event .getEventCategory ());
178
+ } else {
179
+ eventEmitter .receiveEvent (
180
+ event .getSurfaceId (), event .getViewTag (), event .getEventName (), eventData );
181
+ }
182
+ }
183
+ }
184
+
185
+ private static WritableArray getWritableArray (WritableMap ... objects ) {
186
+ return getWritableArray (false , objects );
187
+ }
188
+
189
+ private static WritableArray getWritableArray (boolean copyObjects , WritableMap ... objects ) {
190
+ WritableArray result = new WritableNativeArray ();
191
+ for (WritableMap object : objects ) {
192
+ if (object != null ) {
193
+ result .pushMap (copyObjects ? object .copy () : object );
194
+ }
195
+ }
196
+ return result ;
197
+ }
99
198
}
0 commit comments