Skip to content

Commit 1d6d8fc

Browse files
zeyapmeta-codesync[bot]
authored andcommitted
Include activeHitPath in payload when it's PointerUp/Down (#54562)
Summary: Pull Request resolved: #54562 ## Changelog: [Android] [Added] - Include activeHitPath in payload when it's PointerUp/Down It's only included when using c++ animated, some native event listener will need the hit path information from the platform before it reaches React. Note that the actual bubbling only doesn't happen until in React so there's no way native code can know where bubbling will reach when the event is intercepted on main thread. Reviewed By: javache Differential Revision: D87092392 fbshipit-source-id: d5dcc13c3cec685fc4677b3cb1173a0584ec5d00
1 parent 21b93d8 commit 1d6d8fc

7 files changed

Lines changed: 129 additions & 17 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.facebook.common.logging.FLog;
1616
import com.facebook.infer.annotation.Assertions;
1717
import com.facebook.react.common.ReactConstants;
18+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
1819
import com.facebook.react.uimanager.TouchTargetHelper.ViewTarget;
1920
import com.facebook.react.uimanager.events.EventDispatcher;
2021
import com.facebook.react.uimanager.events.PointerEvent;
@@ -133,9 +134,17 @@ private void onUp(
133134
boolean listeningForUp =
134135
isAnyoneListeningForBubblingEvent(activeHitPath, EVENT.UP, EVENT.UP_CAPTURE);
135136
if (listeningForUp) {
137+
List<Integer> activeHitPathViewIds =
138+
ReactNativeFeatureFlags.cxxNativeAnimatedEnabled()
139+
? eventState.getHitPathViewIdsForActivePointer()
140+
: null;
136141
eventDispatcher.dispatchEvent(
137142
PointerEvent.obtain(
138-
PointerEventHelper.POINTER_UP, activeTargetTag, eventState, motionEvent));
143+
PointerEventHelper.POINTER_UP,
144+
activeTargetTag,
145+
eventState,
146+
motionEvent,
147+
activeHitPathViewIds));
139148
}
140149

141150
boolean supportsHover = mHoveringPointerIds.contains(activePointerId);
@@ -230,9 +239,17 @@ private void onDown(
230239
boolean listeningForDown =
231240
isAnyoneListeningForBubblingEvent(activeHitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE);
232241
if (listeningForDown) {
242+
List<Integer> activeHitPathViewIds =
243+
ReactNativeFeatureFlags.cxxNativeAnimatedEnabled()
244+
? eventState.getHitPathViewIdsForActivePointer()
245+
: null;
233246
eventDispatcher.dispatchEvent(
234247
PointerEvent.obtain(
235-
PointerEventHelper.POINTER_DOWN, activeTargetTag, eventState, motionEvent));
248+
PointerEventHelper.POINTER_DOWN,
249+
activeTargetTag,
250+
eventState,
251+
motionEvent,
252+
activeHitPathViewIds));
236253
}
237254
}
238255

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,22 @@ internal class PointerEvent private constructor() : Event<PointerEvent>() {
3030
private var coalescingKey = UNSET_COALESCING_KEY
3131
private var pointersEventData: List<WritableMap>? = null
3232
private var eventState: PointerEventState? = null
33+
private var activeHitPathViewIds: List<Int>? = null
3334

3435
private fun init(
3536
eventName: String,
3637
targetTag: Int,
3738
eventState: PointerEventState,
3839
motionEventToCopy: MotionEvent,
3940
coalescingKey: Short,
41+
activeHitPathViewIds: List<Int>?,
4042
) {
4143
super.init(eventState.getSurfaceId(), targetTag, motionEventToCopy.eventTime)
4244
this._eventName = eventName
4345
this.motionEvent = MotionEvent.obtain(motionEventToCopy)
4446
this.coalescingKey = coalescingKey
4547
this.eventState = eventState
48+
this.activeHitPathViewIds = activeHitPathViewIds
4649
}
4750

4851
override fun getEventName(): String = _eventName
@@ -213,6 +216,13 @@ internal class PointerEvent private constructor() : Event<PointerEvent>() {
213216
pointerEvent.putDouble("pressure", pressure)
214217
pointerEvent.putDouble("tangentialPressure", 0.0)
215218

219+
if (activeHitPathViewIds != null) {
220+
pointerEvent.putArray(
221+
"hitPathForEventListener",
222+
Arguments.makeNativeArray(activeHitPathViewIds),
223+
)
224+
}
225+
216226
addModifierKeyData(pointerEvent, motionEvent.metaState)
217227

218228
return pointerEvent
@@ -296,6 +306,9 @@ internal class PointerEvent private constructor() : Event<PointerEvent>() {
296306

297307
val hitPathForActivePointer: List<ViewTarget>
298308
get() = checkNotNull(hitPathByPointerId[activePointerId])
309+
310+
val hitPathViewIdsForActivePointer: List<Int>
311+
get() = checkNotNull(hitPathByPointerId[activePointerId]).map { it.getViewId() }
299312
}
300313

301314
companion object {
@@ -310,6 +323,15 @@ internal class PointerEvent private constructor() : Event<PointerEvent>() {
310323
targetTag: Int,
311324
eventState: PointerEventState,
312325
motionEventToCopy: MotionEvent?,
326+
): PointerEvent = obtain(eventName, targetTag, eventState, motionEventToCopy, null)
327+
328+
@JvmStatic
329+
fun obtain(
330+
eventName: String,
331+
targetTag: Int,
332+
eventState: PointerEventState,
333+
motionEventToCopy: MotionEvent?,
334+
activeHitPathViewIds: List<Int>?,
313335
): PointerEvent {
314336
var event = EVENTS_POOL.acquire()
315337
if (event == null) {
@@ -321,6 +343,7 @@ internal class PointerEvent private constructor() : Event<PointerEvent>() {
321343
eventState,
322344
Assertions.assertNotNull(motionEventToCopy),
323345
0.toShort(),
346+
activeHitPathViewIds,
324347
)
325348
return event
326349
}
@@ -343,6 +366,7 @@ internal class PointerEvent private constructor() : Event<PointerEvent>() {
343366
eventState,
344367
Assertions.assertNotNull(motionEventToCopy),
345368
coalescingKey,
369+
null,
346370
)
347371
return event
348372
}

packages/react-native/ReactCommon/react/renderer/core/DynamicEventPayload.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@
88
#include "DynamicEventPayload.h"
99

1010
#include <jsi/JSIDynamic.h>
11+
#include <react/renderer/core/DynamicPointerEvent.h>
1112

1213
namespace facebook::react {
1314

1415
DynamicEventPayload::DynamicEventPayload(folly::dynamic&& payload)
1516
: payload_(std::move(payload)) {}
1617

18+
/* static */ SharedEventPayload DynamicEventPayload::create(
19+
folly::dynamic&& payload) {
20+
if (payload.find("pointerId") != payload.items().end()) {
21+
return std::make_shared<DynamicPointerEvent>(std::move(payload));
22+
} else {
23+
return std::make_shared<DynamicEventPayload>(std::move(payload));
24+
}
25+
}
26+
1727
jsi::Value DynamicEventPayload::asJSIValue(jsi::Runtime& runtime) const {
1828
return jsi::valueFromDynamic(runtime, payload_);
1929
}

packages/react-native/ReactCommon/react/renderer/core/DynamicEventPayload.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,22 @@
1212

1313
namespace facebook::react {
1414

15+
/*
16+
* EventPayload sent from android native via JNI.
17+
*/
1518
struct DynamicEventPayload : public EventPayload {
1619
explicit DynamicEventPayload(folly::dynamic &&payload);
1720

21+
static SharedEventPayload create(folly::dynamic &&payload);
22+
1823
/*
1924
* EventPayload implementations
2025
*/
2126
jsi::Value asJSIValue(jsi::Runtime &runtime) const override;
2227
EventPayloadType getType() const override;
2328
std::optional<double> extractValue(const std::vector<std::string> &path) const override;
2429

25-
private:
30+
protected:
2631
folly::dynamic payload_;
2732
};
2833

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "DynamicPointerEvent.h"
9+
10+
#include <jsi/JSIDynamic.h>
11+
12+
namespace facebook::react {
13+
14+
DynamicPointerEvent::DynamicPointerEvent(folly::dynamic&& payload)
15+
: DynamicEventPayload(std::move(payload)) {
16+
const auto& hitPath = payload_["hitPathForEventListener"];
17+
if (hitPath.type() == folly::dynamic::Type::ARRAY) {
18+
hitPathForEventListener_ = std::vector<Tag>{};
19+
for (const auto& item : hitPath) {
20+
hitPathForEventListener_->push_back(static_cast<Tag>(item.asInt()));
21+
}
22+
}
23+
}
24+
25+
const std::optional<std::vector<Tag>>&
26+
DynamicPointerEvent::getHitPathForEventListener() const {
27+
return hitPathForEventListener_;
28+
}
29+
30+
} // namespace facebook::react
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <folly/dynamic.h>
11+
#include <react/renderer/core/DynamicEventPayload.h>
12+
#include <react/renderer/core/EventPayload.h>
13+
#include <react/renderer/core/ReactPrimitives.h>
14+
15+
namespace facebook::react {
16+
17+
/*
18+
* Payload of PointerEvent sent from android native via JNI.
19+
*/
20+
struct DynamicPointerEvent : public DynamicEventPayload {
21+
explicit DynamicPointerEvent(folly::dynamic &&payload);
22+
23+
const std::optional<std::vector<Tag>> &getHitPathForEventListener() const;
24+
25+
private:
26+
std::optional<std::vector<Tag>> hitPathForEventListener_;
27+
};
28+
29+
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/core/EventEmitter.cpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,19 @@
1010
#include <cxxreact/TraceSection.h>
1111
#include <folly/dynamic.h>
1212
#include <jsi/jsi.h>
13-
14-
#include "DynamicEventPayload.h"
15-
#include "RawEvent.h"
13+
#include <react/renderer/core/DynamicEventPayload.h>
14+
#include <react/renderer/core/RawEvent.h>
1615

1716
namespace facebook::react {
18-
1917
static bool hasPrefix(const std::string& str, const std::string& prefix) {
2018
return str.compare(0, prefix.length(), prefix) == 0;
2119
}
2220

2321
// TODO(T29874519): Get rid of "top" prefix once and for all.
2422
/*
25-
* Replaces "on" with "top" if present. Or capitalizes the first letter and adds
26-
* "top" prefix. E.g. "eventName" becomes "topEventName", "onEventName" also
27-
* becomes "topEventName".
23+
* Replaces "on" with "top" if present. Or capitalizes the first letter and
24+
* adds "top" prefix. E.g. "eventName" becomes "topEventName", "onEventName"
25+
* also becomes "topEventName".
2826
*/
2927
/* static */ std::string EventEmitter::normalizeEventType(std::string type) {
3028
auto prefixedType = std::move(type);
@@ -61,16 +59,15 @@ void EventEmitter::dispatchEvent(
6159
RawEvent::Category category) const {
6260
dispatchEvent(
6361
std::move(type),
64-
std::make_shared<DynamicEventPayload>(std::move(payload)),
62+
DynamicEventPayload::create(std::move(payload)),
6563
category);
6664
}
6765

6866
void EventEmitter::dispatchUniqueEvent(
6967
std::string type,
7068
folly::dynamic&& payload) const {
7169
dispatchUniqueEvent(
72-
std::move(type),
73-
std::make_shared<DynamicEventPayload>(std::move(payload)));
70+
std::move(type), DynamicEventPayload::create(std::move(payload)));
7471
}
7572

7673
void EventEmitter::dispatchEvent(
@@ -139,10 +136,10 @@ void EventEmitter::setEnabled(bool enabled) const {
139136
}
140137
}
141138

142-
// Note: Initially, the state of `eventTarget_` and the value `enableCounter_`
143-
// is mismatched intentionally (it's `non-null` and `0` accordingly). We need
144-
// this to support an initial nebula state where the event target must be
145-
// retained without any associated mounted node.
139+
// Note: Initially, the state of `eventTarget_` and the value
140+
// `enableCounter_` is mismatched intentionally (it's `non-null` and `0`
141+
// accordingly). We need this to support an initial nebula state where the
142+
// event target must be retained without any associated mounted node.
146143
bool shouldBeRetained = enableCounter_ > 0;
147144
if (shouldBeRetained != (eventTarget_ != nullptr)) {
148145
if (!shouldBeRetained) {

0 commit comments

Comments
 (0)