Skip to content

Commit 2095643

Browse files
committed
Events on Android
1 parent 6c9a97a commit 2095643

File tree

9 files changed

+177
-145
lines changed

9 files changed

+177
-145
lines changed

Example/Reanimated/animations/spring.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
or,
1515
neq,
1616
and,
17-
debug,
1817
lessThan,
1918
greaterThan,
2019
} from '../base';

Example/Reanimated/base.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ export const set = function(what, value) {
4141
};
4242

4343
export const cond = function(cond, ifBlock, elseBlock) {
44-
return new AnimatedCond(adapt(cond), adapt(ifBlock), adapt(elseBlock));
44+
return new AnimatedCond(
45+
adapt(cond),
46+
adapt(ifBlock),
47+
elseBlock === undefined ? undefined : adapt(elseBlock)
48+
);
4549
};
4650

4751
export const block = function(items) {

Example/Reanimated/core/AnimatedCond.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default class AnimatedCond extends AnimatedNode {
1212
type: 'cond',
1313
cond: condition.__nodeID,
1414
ifBlock: ifBlock.__nodeID,
15-
elseBlock: elseBlock.__nodeID,
15+
elseBlock: elseBlock ? elseBlock.__nodeID : undefined,
1616
},
1717
[condition, ifBlock, elseBlock]
1818
);

Example/Reanimated/core/AnimatedValue.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import AnimatedNode from './AnimatedNode';
22
import { val } from '../utils';
33

4+
function sanitizeValue(value) {
5+
return value === null || value === undefined ? value : Number(value);
6+
}
7+
48
let _uniqueId = 1;
59

610
export default class AnimatedValue extends AnimatedNode {
711
constructor(value) {
8-
super({ type: 'value', value });
12+
super({ type: 'value', value: sanitizeValue(value) });
913
this._startingValue = this._value = value;
1014
this._offset = 0;
1115
this._animation = null;

Example/android/app/src/main/java/com/swmansion/reanimated/NodesManager.java

Lines changed: 62 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
77
import com.facebook.react.bridge.ReactContext;
88
import com.facebook.react.bridge.ReadableMap;
9+
import com.facebook.react.bridge.UiThreadUtil;
910
import com.facebook.react.modules.core.ReactChoreographer;
1011
import com.facebook.react.uimanager.GuardedFrameCallback;
1112
import com.facebook.react.uimanager.UIImplementation;
@@ -18,6 +19,7 @@
1819
import com.swmansion.reanimated.nodes.ClockOpNode;
1920
import com.swmansion.reanimated.nodes.CondNode;
2021
import com.swmansion.reanimated.nodes.DebugNode;
22+
import com.swmansion.reanimated.nodes.EventNode;
2123
import com.swmansion.reanimated.nodes.Node;
2224
import com.swmansion.reanimated.nodes.OperatorNode;
2325
import com.swmansion.reanimated.nodes.PropsNode;
@@ -27,7 +29,11 @@
2729
import com.swmansion.reanimated.nodes.ValueNode;
2830

2931
import java.util.ArrayList;
32+
import java.util.HashMap;
3033
import java.util.List;
34+
import java.util.Map;
35+
import java.util.concurrent.ConcurrentLinkedQueue;
36+
import java.util.concurrent.atomic.AtomicBoolean;
3137

3238
import 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
}

Example/android/app/src/main/java/com/swmansion/reanimated/ReanimatedModule.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public void disconnectNodeFromView(final int nodeID, final int viewTag) {
146146
mOperations.add(new UIThreadOperation() {
147147
@Override
148148
public void execute(NodesManager nodesManager) {
149-
nodesManager.connectNodeToView(nodeID, viewTag);
149+
nodesManager.disconnectNodeFromView(nodeID, viewTag);
150150
}
151151
});
152152
}
@@ -156,6 +156,7 @@ public void attachEvent(final int viewTag, final String eventName, final int eve
156156
mOperations.add(new UIThreadOperation() {
157157
@Override
158158
public void execute(NodesManager nodesManager) {
159+
nodesManager.attachEvent(viewTag, eventName, eventNodeID);
159160
}
160161
});
161162
}
@@ -165,6 +166,7 @@ public void detachEvent(final int viewTag, final String eventName, final int eve
165166
mOperations.add(new UIThreadOperation() {
166167
@Override
167168
public void execute(NodesManager nodesManager) {
169+
nodesManager.detachEvent(viewTag, eventName, eventNodeID);
168170
}
169171
});
170172
}

Example/android/app/src/main/java/com/swmansion/reanimated/nodes/CondNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ protected Object evaluate() {
1919
Object cond = mNodesManager.findNodeById(mCondID).value();
2020
if (cond instanceof Number && ((Number) cond).doubleValue() != 0.0) {
2121
// This is not a good way to compare doubles but in this case it is what we want
22-
return mIfBlockID != -1 ? mNodesManager.findNodeById(mIfBlockID).value() : 0;
22+
return mIfBlockID != -1 ? mNodesManager.findNodeById(mIfBlockID).value() : 0.;
2323
}
24-
return mElseBlockID != -1 ? mNodesManager.findNodeById(mElseBlockID).value() : 0;
24+
return mElseBlockID != -1 ? mNodesManager.findNodeById(mElseBlockID).value() : 0.;
2525
}
2626
}

0 commit comments

Comments
 (0)