Refactor EventDriverAnimations to customize event match

Summary:
Changelog: [Internal] - Refactor match logic on determining whether to run an EventAnimationDriver (drivers for natively animated events) for an Event dispatched.

Previously, drivers were stored by key on the NativeAnimatedNodesManager (based on event handler and viewTag) and has been refactored to be stored in a list for easier matching.

This diff changes it so the match logic for running an EventAnimationDriver happens on the Event instance. This change is motivated by PointerEvents needing custom match logic (done on a following change).

Reviewed By: javache

Differential Revision: D40691002

fbshipit-source-id: e4f6742a2af3b751214aefa1fc069f65e8e71d77
This commit is contained in:
Luna Wei
2022-11-01 12:50:05 -07:00
committed by Facebook GitHub Bot
parent c565a770eb
commit 565a7439ac
3 changed files with 67 additions and 39 deletions
@@ -21,8 +21,13 @@ import java.util.List;
/* package */ class EventAnimationDriver implements RCTEventEmitter {
private List<String> mEventPath;
/* package */ ValueAnimatedNode mValueNode;
/* package */ String mEventName;
/* package */ int mViewTag;
public EventAnimationDriver(List<String> eventPath, ValueAnimatedNode valueNode) {
public EventAnimationDriver(
String eventName, int viewTag, List<String> eventPath, ValueAnimatedNode valueNode) {
mEventName = eventName;
mViewTag = viewTag;
mEventPath = eventPath;
mValueNode = valueNode;
}
@@ -31,11 +31,9 @@ import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.EventDispatcherListener;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Queue;
/**
@@ -58,9 +56,9 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
private final SparseArray<AnimatedNode> mAnimatedNodes = new SparseArray<>();
private final SparseArray<AnimationDriver> mActiveAnimations = new SparseArray<>();
private final SparseArray<AnimatedNode> mUpdatedNodes = new SparseArray<>();
// Mapping of a view tag and an event name to a list of event animation drivers. 99% of the time
// there will be only one driver per mapping so all code code should be optimized around that.
private final Map<String, List<EventAnimationDriver>> mEventDrivers = new HashMap<>();
// List of event animation drivers for an event on view.
// There may be multiple drivers for the same event and view.
private final List<EventAnimationDriver> mEventDrivers = new ArrayList<>();
private final ReactApplicationContext mReactApplicationContext;
private int mAnimatedGraphBFSColor = 0;
// Used to avoid allocating a new array on every frame in `runUpdates` and `onEventDispatch`.
@@ -501,7 +499,8 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
}
@UiThread
public void addAnimatedEventToView(int viewTag, String eventName, ReadableMap eventMapping) {
public void addAnimatedEventToView(
int viewTag, String eventHandlerName, ReadableMap eventMapping) {
int nodeTag = eventMapping.getInt("animatedValueTag");
AnimatedNode node = mAnimatedNodes.get(nodeTag);
if (node == null) {
@@ -512,8 +511,8 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
throw new JSApplicationIllegalArgumentException(
"addAnimatedEventToView: Animated node on view ["
+ viewTag
+ "] connected to event ("
+ eventName
+ "] connected to event handler ("
+ eventHandlerName
+ ") should be of type "
+ ValueAnimatedNode.class.getName());
}
@@ -524,32 +523,27 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
pathList.add(path.getString(i));
}
EventAnimationDriver event = new EventAnimationDriver(pathList, (ValueAnimatedNode) node);
String key = viewTag + eventName;
if (mEventDrivers.containsKey(key)) {
mEventDrivers.get(key).add(event);
} else {
List<EventAnimationDriver> drivers = new ArrayList<>(1);
drivers.add(event);
mEventDrivers.put(key, drivers);
}
String eventName = normalizeEventName(eventHandlerName);
EventAnimationDriver eventDriver =
new EventAnimationDriver(eventName, viewTag, pathList, (ValueAnimatedNode) node);
mEventDrivers.add(eventDriver);
}
@UiThread
public void removeAnimatedEventFromView(int viewTag, String eventName, int animatedValueTag) {
String key = viewTag + eventName;
if (mEventDrivers.containsKey(key)) {
List<EventAnimationDriver> driversForKey = mEventDrivers.get(key);
if (driversForKey.size() == 1) {
mEventDrivers.remove(viewTag + eventName);
} else {
ListIterator<EventAnimationDriver> it = driversForKey.listIterator();
while (it.hasNext()) {
if (it.next().mValueNode.mTag == animatedValueTag) {
it.remove();
break;
}
}
public void removeAnimatedEventFromView(
int viewTag, String eventHandlerName, int animatedValueTag) {
String eventName = normalizeEventName(eventHandlerName);
ListIterator<EventAnimationDriver> it = mEventDrivers.listIterator();
while (it.hasNext()) {
EventAnimationDriver driver = it.next();
if (eventName.equals(driver.mEventName)
&& viewTag == driver.mViewTag
&& animatedValueTag == driver.mValueNode.mTag) {
it.remove();
break;
}
}
}
@@ -585,18 +579,19 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
if (uiManager == null) {
return;
}
String eventName = uiManager.resolveCustomDirectEventName(event.getEventName());
if (eventName == null) {
eventName = "";
}
List<EventAnimationDriver> driversForKey = mEventDrivers.get(event.getViewTag() + eventName);
if (driversForKey != null) {
for (EventAnimationDriver driver : driversForKey) {
boolean foundAtLeastOneDriver = false;
Event.EventAnimationDriverMatchSpec matchSpec = event.getEventAnimationDriverMatchSpec();
for (EventAnimationDriver driver : mEventDrivers) {
if (matchSpec.match(driver.mViewTag, driver.mEventName)) {
foundAtLeastOneDriver = true;
stopAnimationsForNode(driver.mValueNode);
event.dispatch(driver);
mRunUpdateNodeList.add(driver.mValueNode);
}
}
if (foundAtLeastOneDriver) {
updateNodes(mRunUpdateNodeList);
mRunUpdateNodeList.clear();
}
@@ -824,4 +819,14 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
mWarnedAboutGraphTraversal = false;
}
}
private String normalizeEventName(String eventHandlerName) {
// Fabric UIManager also makes this assumption
String eventName = eventHandlerName;
if (eventHandlerName.startsWith("on")) {
eventName = "top" + eventHandlerName.substring(2);
}
return eventName;
}
}
@@ -40,6 +40,7 @@ public abstract class Event<T extends Event> {
private int mViewTag;
private long mTimestampMs;
private int mUniqueID = sUniqueID++;
private @Nullable EventAnimationDriverMatchSpec mEventAnimationDriverMatchSpec;
protected Event() {}
@@ -165,6 +166,19 @@ public abstract class Event<T extends Event> {
/** @return the name of this event as registered in JS */
public abstract String getEventName();
public EventAnimationDriverMatchSpec getEventAnimationDriverMatchSpec() {
if (mEventAnimationDriverMatchSpec == null) {
mEventAnimationDriverMatchSpec =
new EventAnimationDriverMatchSpec() {
@Override
public boolean match(int viewTag, String eventName) {
return viewTag == getViewTag() && eventName.equals(getEventName());
};
};
}
return mEventAnimationDriverMatchSpec;
}
/**
* Dispatch this event to JS using the given event emitter. Compatible with old and new renderer.
* Instead of using this or dispatchModern, it is recommended that you simply override
@@ -225,4 +239,8 @@ public abstract class Event<T extends Event> {
}
dispatch(rctEventEmitter);
}
public interface EventAnimationDriverMatchSpec {
boolean match(int viewTag, String eventName);
}
}