66import com .facebook .react .bridge .JSApplicationIllegalArgumentException ;
77import com .facebook .react .bridge .ReactContext ;
88import com .facebook .react .bridge .ReadableMap ;
9+ import com .facebook .react .bridge .UiThreadUtil ;
910import com .facebook .react .modules .core .ReactChoreographer ;
1011import com .facebook .react .uimanager .GuardedFrameCallback ;
1112import com .facebook .react .uimanager .UIImplementation ;
1819import com .swmansion .reanimated .nodes .ClockOpNode ;
1920import com .swmansion .reanimated .nodes .CondNode ;
2021import com .swmansion .reanimated .nodes .DebugNode ;
22+ import com .swmansion .reanimated .nodes .EventNode ;
2123import com .swmansion .reanimated .nodes .Node ;
2224import com .swmansion .reanimated .nodes .OperatorNode ;
2325import com .swmansion .reanimated .nodes .PropsNode ;
2729import com .swmansion .reanimated .nodes .ValueNode ;
2830
2931import java .util .ArrayList ;
32+ import java .util .HashMap ;
3033import java .util .List ;
34+ import java .util .Map ;
35+ import java .util .concurrent .ConcurrentLinkedQueue ;
36+ import java .util .concurrent .atomic .AtomicBoolean ;
3137
3238import javax .annotation .Nullable ;
3339
@@ -38,12 +44,15 @@ public interface OnAnimationFrame {
3844 }
3945
4046 private final SparseArray <Node > mAnimatedNodes = new SparseArray <>();
47+ private final Map <String , EventNode > mEventMapping = new HashMap <>();
4148 private final UIImplementation mUIImplementation ;
4249 private final ReactChoreographer mReactChoreographer ;
4350 private final GuardedFrameCallback mChoreographerCallback ;
51+ private final UIManagerModule .CustomEventNamesResolver mCustomEventNamesResolver ;
52+ private final AtomicBoolean mCallbackPosted = new AtomicBoolean ();
4453
4554 private List <OnAnimationFrame > mFrameCallbacks = new ArrayList <>();
46- private boolean mCallbackPosted ;
55+ private ConcurrentLinkedQueue < Event > mEventQueue = new ConcurrentLinkedQueue <>() ;
4756 private boolean mWantRunUpdates ;
4857
4958 public double currentFrameTimeMs ;
@@ -53,6 +62,7 @@ public NodesManager(ReactContext context) {
5362 UIManagerModule uiManager = context .getNativeModule (UIManagerModule .class );
5463 updateContext = new UpdateContext ();
5564 mUIImplementation = uiManager .getUIImplementation ();
65+ mCustomEventNamesResolver = uiManager .getDirectEventNamesResolver ();
5666 uiManager .getEventDispatcher ().addListener (this );
5767
5868 mReactChoreographer = ReactChoreographer .getInstance ();
@@ -65,31 +75,28 @@ protected void doFrameGuarded(long frameTimeNanos) {
6575 }
6676
6777 public void onHostPause () {
68- if (mCallbackPosted ) {
78+ if (mCallbackPosted . get () ) {
6979 stopUpdatingOnAnimationFrame ();
70- mCallbackPosted = true ;
80+ mCallbackPosted . set ( true ) ;
7181 }
7282 }
7383
7484 public void onHostResume () {
75- if (mCallbackPosted ) {
76- mCallbackPosted = false ;
85+ if (mCallbackPosted .getAndSet (false )) {
7786 startUpdatingOnAnimationFrame ();
7887 }
7988 }
8089
8190 private void startUpdatingOnAnimationFrame () {
82- if (!mCallbackPosted ) {
83- mCallbackPosted = true ;
91+ if (!mCallbackPosted .getAndSet (true )) {
8492 mReactChoreographer .postFrameCallback (
8593 ReactChoreographer .CallbackType .NATIVE_ANIMATED_MODULE ,
8694 mChoreographerCallback );
8795 }
8896 }
8997
9098 private void stopUpdatingOnAnimationFrame () {
91- if (mCallbackPosted ) {
92- mCallbackPosted = false ;
99+ if (mCallbackPosted .getAndSet (false )) {
93100 mReactChoreographer .removeFrameCallback (
94101 ReactChoreographer .CallbackType .NATIVE_ANIMATED_MODULE ,
95102 mChoreographerCallback );
@@ -98,22 +105,27 @@ private void stopUpdatingOnAnimationFrame() {
98105
99106 private void onAnimationFrame (long frameTimeNanos ) {
100107 currentFrameTimeMs = frameTimeNanos / 1000000. ;
101- // TODO: process enqueued events
102108
103- List <OnAnimationFrame > frameCallbacks = mFrameCallbacks ;
104- mFrameCallbacks = new ArrayList <>(frameCallbacks .size ());
105- for (int i = 0 , size = frameCallbacks .size (); i < size ; i ++) {
106- frameCallbacks .get (i ).onAnimationFrame ();
109+ while (!mEventQueue .isEmpty ()) {
110+ handleEvent (mEventQueue .poll ());
111+ }
112+
113+ if (!mFrameCallbacks .isEmpty ()) {
114+ List <OnAnimationFrame > frameCallbacks = mFrameCallbacks ;
115+ mFrameCallbacks = new ArrayList <>(frameCallbacks .size ());
116+ for (int i = 0 , size = frameCallbacks .size (); i < size ; i ++) {
117+ frameCallbacks .get (i ).onAnimationFrame ();
118+ }
107119 }
108120
109121 if (mWantRunUpdates ) {
110122 Node .runUpdates (updateContext );
111123 }
112124
113- mCallbackPosted = false ;
125+ mCallbackPosted . set ( false ) ;
114126 mWantRunUpdates = false ;
115127
116- if (!mFrameCallbacks .isEmpty ()) {
128+ if (!mFrameCallbacks .isEmpty () || ! mEventQueue . isEmpty () ) {
117129 // enqueue next frame
118130 startUpdatingOnAnimationFrame ();
119131 }
@@ -161,7 +173,7 @@ public void createNode(int nodeID, ReadableMap config) {
161173 } else if ("bezier" .equals (type )) {
162174 node = new BezierNode (nodeID , config , this );
163175 } else if ("event" .equals (type )) {
164- throw new JSApplicationIllegalArgumentException ( "Unsupported node type: " + type );
176+ node = new EventNode ( nodeID , config , this );
165177 } else {
166178 throw new JSApplicationIllegalArgumentException ("Unsupported node type: " + type );
167179 }
@@ -227,53 +239,24 @@ public void disconnectNodeFromView(int nodeID, int viewTag) {
227239 }
228240
229241 public void attachEvent (int viewTag , String eventName , int eventNodeID ) {
230- // int nodeTag = eventMapping.getInt("animatedValueTag");
231- // AnimatedNode node = mAnimatedNodes.get(nodeTag);
232- // if (node == null) {
233- // throw new JSApplicationIllegalArgumentException("Animated node with tag " + nodeTag +
234- // " does not exists");
235- // }
236- // if (!(node instanceof ValueAnimatedNode)) {
237- // throw new JSApplicationIllegalArgumentException("Animated node connected to event should be" +
238- // "of type " + ValueAnimatedNode.class.getName());
239- // }
240- //
241- // ReadableArray path = eventMapping.getArray("nativeEventPath");
242- // List<String> pathList = new ArrayList<>(path.size());
243- // for (int i = 0; i < path.size(); i++) {
244- // pathList.add(path.getString(i));
245- // }
246- //
247- // EventAnimationDriver event = new EventAnimationDriver(pathList, (ValueAnimatedNode) node);
248- // String key = viewTag + eventName;
249- // if (mEventDrivers.containsKey(key)) {
250- // mEventDrivers.get(key).add(event);
251- // } else {
252- // List<EventAnimationDriver> drivers = new ArrayList<>(1);
253- // drivers.add(event);
254- // mEventDrivers.put(key, drivers);
255- // }
242+ String key = viewTag + eventName ;
243+
244+ EventNode node = (EventNode ) mAnimatedNodes .get (eventNodeID );
245+ if (node == null ) {
246+ throw new JSApplicationIllegalArgumentException ("Event node " + eventNodeID + " does not exists" );
247+ }
248+ if (mEventMapping .containsKey (key )) {
249+ throw new JSApplicationIllegalArgumentException ("Event handler already set for the given view and event type" );
250+ }
251+
252+ mEventMapping .put (key , node );
256253 }
257254
258255 public void detachEvent (int viewTag , String eventName , int eventNodeID ) {
259- // String key = viewTag + eventName;
260- // if (mEventDrivers.containsKey(key)) {
261- // List<EventAnimationDriver> driversForKey = mEventDrivers.get(key);
262- // if (driversForKey.size() == 1) {
263- // mEventDrivers.remove(viewTag + eventName);
264- // } else {
265- // ListIterator<EventAnimationDriver> it = driversForKey.listIterator();
266- // while (it.hasNext()) {
267- // if (it.next().mValueNode.mTag == animatedValueTag) {
268- // it.remove();
269- // break;
270- // }
271- // }
272- // }
273- // }
256+ String key = viewTag + eventName ;
257+ mEventMapping .remove (key );
274258 }
275259
276-
277260 public void postRunUpdatesAfterAnimation () {
278261 mWantRunUpdates = true ;
279262 startUpdatingOnAnimationFrame ();
@@ -285,77 +268,27 @@ public void postOnAnimation(OnAnimationFrame onAnimationFrame) {
285268 }
286269
287270 @ Override
288- public void onEventDispatch (final Event event ) {
289- // // Events can be dispatched from any thread so we have to make sure handleEvent is run from the
290- // // UI thread.
291- // if (UiThreadUtil.isOnUiThread()) {
292- // handleEvent(event);
293- // } else {
294- // UiThreadUtil.runOnUiThread(new Runnable() {
295- // @Override
296- // public void run() {
297- // handleEvent(event);
298- // }
299- // });
300- // }
301- // }
302- //
303- // private void handleEvent(Event event) {
304- // if (!mEventDrivers.isEmpty()) {
305- // // If the event has a different name in native convert it to it's JS name.
306- // String eventName = mCustomEventNamesResolver.resolveCustomEventName(event.getEventName());
307- // List<EventAnimationDriver> driversForKey = mEventDrivers.get(event.getViewTag() + eventName);
308- // if (driversForKey != null) {
309- // for (EventAnimationDriver driver : driversForKey) {
310- // stopAnimationsForNode(driver.mValueNode);
311- // event.dispatch(driver);
312- // mRunUpdateNodeList.add(driver.mValueNode);
313- // }
314- // updateNodes(mRunUpdateNodeList);
315- // mRunUpdateNodeList.clear();
316- // }
317- // }
271+ public void onEventDispatch (Event event ) {
272+ // Events can be dispatched from any thread so we have to make sure handleEvent is run from the
273+ // UI thread.
274+ if (UiThreadUtil .isOnUiThread ()) {
275+ handleEvent (event );
276+ } else {
277+ mEventQueue .offer (event );
278+ startUpdatingOnAnimationFrame ();
279+ }
318280 }
319281
320- public void runUpdates (long frameTimeNanos ) {
321- // UiThreadUtil.assertOnUiThread();
322- // boolean hasFinishedAnimations = false;
323- //
324- // for (int i = 0; i < mUpdatedNodes.size(); i++) {
325- // AnimatedNode node = mUpdatedNodes.valueAt(i);
326- // mRunUpdateNodeList.add(node);
327- // }
328- //
329- // // Clean mUpdatedNodes queue
330- // mUpdatedNodes.clear();
331- //
332- // for (int i = 0; i < mActiveAnimations.size(); i++) {
333- // AnimationDriver animation = mActiveAnimations.valueAt(i);
334- // animation.runAnimationStep(frameTimeNanos);
335- // AnimatedNode valueNode = animation.mAnimatedValue;
336- // mRunUpdateNodeList.add(valueNode);
337- // if (animation.mHasFinished) {
338- // hasFinishedAnimations = true;
339- // }
340- // }
341- //
342- // updateNodes(mRunUpdateNodeList);
343- // mRunUpdateNodeList.clear();
344- //
345- // // Cleanup finished animations. Iterate over the array of animations and override ones that has
346- // // finished, then resize `mActiveAnimations`.
347- // if (hasFinishedAnimations) {
348- // for (int i = mActiveAnimations.size() - 1; i >= 0; i--) {
349- // AnimationDriver animation = mActiveAnimations.valueAt(i);
350- // if (animation.mHasFinished) {
351- // if (animation.mEndCallback != null) {
352- // WritableMap endCallbackResponse = Arguments.createMap();
353- // endCallbackResponse.putBoolean("finished", true);
354- // animation.mEndCallback.invoke(endCallbackResponse);
355- // }
356- // mActiveAnimations.removeAt(i);
357- // }
358- // }
359- // }
282+ private void handleEvent (Event event ) {
283+ if (!mEventMapping .isEmpty ()) {
284+ // If the event has a different name in native convert it to it's JS name.
285+ String eventName = mCustomEventNamesResolver .resolveCustomEventName (event .getEventName ());
286+ int viewTag = event .getViewTag ();
287+ String key = viewTag + eventName ;
288+ EventNode node = mEventMapping .get (key );
289+ if (node != null ) {
290+ event .dispatch (node );
291+ }
292+ }
360293 }
361294}
0 commit comments