@truewatchtech/react-native-mobile 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FTMobileReactNativeSDK.podspec +7 -5
- package/android/build.gradle +10 -3
- package/android/src/main/java/com/ft/sdk/reactnative/FTLogImpl.java +10 -2
- package/android/src/main/java/com/ft/sdk/reactnative/FTMobileImpl.java +45 -10
- package/android/src/main/java/com/ft/sdk/reactnative/FTRUMImpl.java +131 -6
- package/android/src/main/java/com/ft/sdk/reactnative/FTTraceImpl.java +10 -3
- package/android/src/main/java/com/ft/sdk/reactnative/JsLongTaskMonitor.java +281 -0
- package/android/src/main/java/com/ft/sdk/reactnative/ViewTiming.java +17 -0
- package/android/src/main/java/com/ft/sdk/reactnative/VisitorIdConfigMapper.java +50 -0
- package/android/src/main/java/com/ft/sdk/reactnative/utils/ReactNativeUtils.java +5 -12
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTMobileModule.java +14 -5
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTRUMModule.java +44 -1
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTTraceModule.java +14 -0
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTMobileModule.java +7 -2
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTRUMModule.java +39 -1
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTTraceModule.java +12 -0
- package/android/src/rnpost70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +21 -0
- package/android/src/rnpre70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +37 -0
- package/android/src/test/java/com/ft/sdk/reactnative/FTMobileImplTest.java +53 -0
- package/android/src/test/java/com/ft/sdk/reactnative/FTRUMModuleTest.java +117 -0
- package/android/src/test/java/com/ft/sdk/reactnative/JsLongTaskMonitorTest.java +446 -0
- package/android/src/test/java/com/ft/sdk/reactnative/utils/ReactNativeUtilsTest.java +33 -0
- package/ios/FTJSLongTaskMonitor.h +51 -0
- package/ios/FTJSLongTaskMonitor.m +250 -0
- package/ios/FTMobileReactNative.mm +54 -18
- package/ios/FTReactNativeLog.mm +9 -5
- package/ios/FTReactNativeRUM.mm +196 -31
- package/ios/FTReactNativeResource.h +4 -0
- package/ios/FTReactNativeResource.m +64 -0
- package/ios/FTReactNativeTrace.mm +59 -14
- package/ios/FTReactNativeUtils.m +10 -15
- package/ios/FTReactNativeViewTiming.h +18 -0
- package/ios/FTReactNativeWebSocket.h +10 -0
- package/ios/FTReactNativeWebSocket.mm +201 -0
- package/ios/FTWebSocketMetadataStore.h +25 -0
- package/ios/FTWebSocketMetadataStore.m +622 -0
- package/ios/FTWebSocketResourceData.h +20 -0
- package/ios/FTWebSocketResourceData.m +106 -0
- package/lib/commonjs/ft_logger.js +3 -1
- package/lib/commonjs/ft_logger.js.map +1 -1
- package/lib/commonjs/ft_mobile_agent.js +47 -4
- package/lib/commonjs/ft_mobile_agent.js.map +1 -1
- package/lib/commonjs/ft_rum.js +65 -5
- package/lib/commonjs/ft_rum.js.map +1 -1
- package/lib/commonjs/ft_tracing.js +14 -4
- package/lib/commonjs/ft_tracing.js.map +1 -1
- package/lib/commonjs/index.js +21 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/rum/FTBabelInteractionTracking.js +72 -0
- package/lib/commonjs/rum/FTBabelInteractionTracking.js.map +1 -0
- package/lib/commonjs/rum/FTBabelUtils.js +87 -0
- package/lib/commonjs/rum/FTBabelUtils.js.map +1 -0
- package/lib/commonjs/rum/FTRumActionTracking.js +10 -39
- package/lib/commonjs/rum/FTRumActionTracking.js.map +1 -1
- package/lib/commonjs/rum/FTRumActionTrackingContext.js +50 -0
- package/lib/commonjs/rum/FTRumActionTrackingContext.js.map +1 -0
- package/lib/commonjs/rum/FTRumViewTracking.js +147 -0
- package/lib/commonjs/rum/FTRumViewTracking.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketNative.js +73 -0
- package/lib/commonjs/rum/FTRumWebSocketNative.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketTiming.js +73 -0
- package/lib/commonjs/rum/FTRumWebSocketTiming.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketTracking.js +506 -0
- package/lib/commonjs/rum/FTRumWebSocketTracking.js.map +1 -0
- package/lib/commonjs/rum/longTasksUtils.js +25 -0
- package/lib/commonjs/rum/longTasksUtils.js.map +1 -0
- package/lib/commonjs/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeTrace.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js +11 -0
- package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/commonjs/visitorIdConfig.js +37 -0
- package/lib/commonjs/visitorIdConfig.js.map +1 -0
- package/lib/module/ft_logger.js +3 -1
- package/lib/module/ft_logger.js.map +1 -1
- package/lib/module/ft_mobile_agent.js +47 -4
- package/lib/module/ft_mobile_agent.js.map +1 -1
- package/lib/module/ft_rum.js +65 -5
- package/lib/module/ft_rum.js.map +1 -1
- package/lib/module/ft_tracing.js +14 -2
- package/lib/module/ft_tracing.js.map +1 -1
- package/lib/module/index.js +4 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/rum/FTBabelInteractionTracking.js +66 -0
- package/lib/module/rum/FTBabelInteractionTracking.js.map +1 -0
- package/lib/module/rum/FTBabelUtils.js +80 -0
- package/lib/module/rum/FTBabelUtils.js.map +1 -0
- package/lib/module/rum/FTRumActionTracking.js +10 -39
- package/lib/module/rum/FTRumActionTracking.js.map +1 -1
- package/lib/module/rum/FTRumActionTrackingContext.js +43 -0
- package/lib/module/rum/FTRumActionTrackingContext.js.map +1 -0
- package/lib/module/rum/FTRumViewTracking.js +140 -0
- package/lib/module/rum/FTRumViewTracking.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketNative.js +62 -0
- package/lib/module/rum/FTRumWebSocketNative.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketTiming.js +67 -0
- package/lib/module/rum/FTRumWebSocketTiming.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketTracking.js +499 -0
- package/lib/module/rum/FTRumWebSocketTracking.js.map +1 -0
- package/lib/module/rum/longTasksUtils.js +18 -0
- package/lib/module/rum/longTasksUtils.js.map +1 -0
- package/lib/module/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeTrace.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeWebSocket.js +8 -0
- package/lib/module/specs/NativeFTReactNativeWebSocket.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/module/visitorIdConfig.js +31 -0
- package/lib/module/visitorIdConfig.js.map +1 -0
- package/lib/typescript/ft_logger.d.ts +11 -1
- package/lib/typescript/ft_mobile_agent.d.ts +28 -0
- package/lib/typescript/ft_rum.d.ts +42 -5
- package/lib/typescript/ft_tracing.d.ts +9 -1
- package/lib/typescript/index.d.ts +5 -1
- package/lib/typescript/rum/FTBabelInteractionTracking.d.ts +30 -0
- package/lib/typescript/rum/FTBabelUtils.d.ts +1 -0
- package/lib/typescript/rum/FTRumActionTracking.d.ts +0 -2
- package/lib/typescript/rum/FTRumActionTrackingContext.d.ts +11 -0
- package/lib/typescript/rum/FTRumViewTracking.d.ts +32 -0
- package/lib/typescript/rum/FTRumWebSocketNative.d.ts +22 -0
- package/lib/typescript/rum/FTRumWebSocketTiming.d.ts +7 -0
- package/lib/typescript/rum/FTRumWebSocketTracking.d.ts +54 -0
- package/lib/typescript/rum/longTasksUtils.d.ts +8 -0
- package/lib/typescript/specs/NativeFTMobileReactNative.d.ts +15 -0
- package/lib/typescript/specs/NativeFTReactNativeRUM.d.ts +11 -1
- package/lib/typescript/specs/NativeFTReactNativeTrace.d.ts +12 -0
- package/lib/typescript/specs/NativeFTReactNativeWebSocket.d.ts +13 -0
- package/lib/typescript/version.d.ts +1 -1
- package/lib/typescript/visitorIdConfig.d.ts +11 -0
- package/package.json +6 -4
- package/src/ft_logger.tsx +11 -1
- package/src/ft_mobile_agent.tsx +79 -7
- package/src/ft_rum.tsx +111 -7
- package/src/ft_tracing.tsx +24 -2
- package/src/index.tsx +8 -0
- package/src/rum/FTBabelInteractionTracking.ts +124 -0
- package/src/rum/FTBabelUtils.tsx +99 -0
- package/src/rum/FTRumActionTracking.tsx +14 -43
- package/src/rum/FTRumActionTrackingContext.ts +68 -0
- package/src/rum/FTRumViewTracking.ts +179 -0
- package/src/rum/FTRumWebSocketNative.ts +107 -0
- package/src/rum/FTRumWebSocketTiming.ts +82 -0
- package/src/rum/FTRumWebSocketTracking.ts +722 -0
- package/src/rum/longTasksUtils.ts +17 -0
- package/src/specs/NativeFTMobileReactNative.ts +18 -0
- package/src/specs/NativeFTReactNativeRUM.ts +19 -1
- package/src/specs/NativeFTReactNativeTrace.ts +12 -0
- package/src/specs/NativeFTReactNativeWebSocket.ts +33 -0
- package/src/version.ts +1 -1
- package/src/visitorIdConfig.ts +123 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
package com.ft.sdk.reactnative;
|
|
2
|
+
|
|
3
|
+
import android.view.Choreographer;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.ft.sdk.FTRUMGlobalManager;
|
|
7
|
+
|
|
8
|
+
import java.util.HashMap;
|
|
9
|
+
import java.util.Map;
|
|
10
|
+
import java.util.Collections;
|
|
11
|
+
import java.util.concurrent.atomic.AtomicBoolean;
|
|
12
|
+
|
|
13
|
+
final class JsLongTaskMonitor {
|
|
14
|
+
|
|
15
|
+
private static final double NANOS_PER_MILLISECOND = 1_000_000d;
|
|
16
|
+
|
|
17
|
+
interface Executor {
|
|
18
|
+
boolean runOnJsThread(Runnable runnable);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface FrameCallback {
|
|
22
|
+
void doFrame(long frameTimeNanos);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface FrameScheduler {
|
|
26
|
+
void postFrameCallback();
|
|
27
|
+
|
|
28
|
+
void removeFrameCallback();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface FrameSchedulerFactory {
|
|
32
|
+
FrameScheduler create(FrameCallback callback);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface Reporter {
|
|
36
|
+
void reportLongTask(long durationNanos, Map<String, Object> property);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private final Executor executor;
|
|
40
|
+
private final FrameSchedulerFactory schedulerFactory;
|
|
41
|
+
private final Reporter reporter;
|
|
42
|
+
private final Object stateLock = new Object();
|
|
43
|
+
private final FrameCallback frameCallback;
|
|
44
|
+
|
|
45
|
+
private volatile long thresholdNanos;
|
|
46
|
+
private volatile int generation;
|
|
47
|
+
private volatile boolean requestedRunning;
|
|
48
|
+
private volatile Map<String, Object> bridgeContext = Collections.emptyMap();
|
|
49
|
+
|
|
50
|
+
// Accessed only on the React Native JavaScript thread.
|
|
51
|
+
private FrameScheduler scheduler;
|
|
52
|
+
private int activeGeneration;
|
|
53
|
+
private long lastFrameTimeNanos;
|
|
54
|
+
|
|
55
|
+
static JsLongTaskMonitor create(ReactApplicationContext reactContext) {
|
|
56
|
+
return new JsLongTaskMonitor(
|
|
57
|
+
new ReactJsThreadExecutor(reactContext),
|
|
58
|
+
new ChoreographerSchedulerFactory(),
|
|
59
|
+
new NativeLongTaskReporter()
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
JsLongTaskMonitor(
|
|
64
|
+
Executor executor,
|
|
65
|
+
FrameSchedulerFactory schedulerFactory,
|
|
66
|
+
Reporter reporter
|
|
67
|
+
) {
|
|
68
|
+
this.executor = executor;
|
|
69
|
+
this.schedulerFactory = schedulerFactory;
|
|
70
|
+
this.reporter = reporter;
|
|
71
|
+
this.frameCallback = new FrameCallback() {
|
|
72
|
+
@Override
|
|
73
|
+
public void doFrame(long frameTimeNanos) {
|
|
74
|
+
handleFrame(frameTimeNanos);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
void setThresholdMilliseconds(double thresholdMilliseconds) {
|
|
80
|
+
thresholdNanos = thresholdMilliseconds <= 0
|
|
81
|
+
? 0
|
|
82
|
+
: (long) (thresholdMilliseconds * NANOS_PER_MILLISECOND);
|
|
83
|
+
if (thresholdNanos == 0) {
|
|
84
|
+
stop();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void setBridgeContext(Map<String, Object> context) {
|
|
89
|
+
bridgeContext = context == null
|
|
90
|
+
? Collections.<String, Object>emptyMap()
|
|
91
|
+
: new HashMap<>(context);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void start() {
|
|
95
|
+
final int startGeneration;
|
|
96
|
+
synchronized (stateLock) {
|
|
97
|
+
if (thresholdNanos == 0) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
requestedRunning = true;
|
|
101
|
+
startGeneration = ++generation;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
boolean accepted = executor.runOnJsThread(new Runnable() {
|
|
105
|
+
@Override
|
|
106
|
+
public void run() {
|
|
107
|
+
if (!isCurrentRequest(startGeneration, true)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
if (scheduler != null) {
|
|
112
|
+
scheduler.removeFrameCallback();
|
|
113
|
+
}
|
|
114
|
+
scheduler = schedulerFactory.create(frameCallback);
|
|
115
|
+
activeGeneration = startGeneration;
|
|
116
|
+
lastFrameTimeNanos = 0;
|
|
117
|
+
scheduler.postFrameCallback();
|
|
118
|
+
} catch (IllegalStateException ignored) {
|
|
119
|
+
disableRequestOnJsThread(startGeneration);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
if (!accepted) {
|
|
124
|
+
markRequestDisabled(startGeneration);
|
|
125
|
+
}
|
|
126
|
+
} catch (IllegalStateException ignored) {
|
|
127
|
+
markRequestDisabled(startGeneration);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
void stop() {
|
|
132
|
+
stop(null);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Unlike a lifecycle pause, SDK shutdown must prevent automatic restarts.
|
|
136
|
+
void disable(final Runnable completion) {
|
|
137
|
+
thresholdNanos = 0;
|
|
138
|
+
stop(completion);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void stop(final Runnable completion) {
|
|
142
|
+
final Runnable completeOnce = once(completion);
|
|
143
|
+
final int stopGeneration;
|
|
144
|
+
synchronized (stateLock) {
|
|
145
|
+
requestedRunning = false;
|
|
146
|
+
stopGeneration = ++generation;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
boolean accepted = executor.runOnJsThread(new Runnable() {
|
|
150
|
+
@Override
|
|
151
|
+
public void run() {
|
|
152
|
+
if (isCurrentRequest(stopGeneration, false)) {
|
|
153
|
+
try {
|
|
154
|
+
if (scheduler != null) {
|
|
155
|
+
scheduler.removeFrameCallback();
|
|
156
|
+
}
|
|
157
|
+
} catch (IllegalStateException ignored) {
|
|
158
|
+
// The request is already disabled, so only local state needs clearing.
|
|
159
|
+
}
|
|
160
|
+
scheduler = null;
|
|
161
|
+
activeGeneration = 0;
|
|
162
|
+
lastFrameTimeNanos = 0;
|
|
163
|
+
}
|
|
164
|
+
completeOnce.run();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if (!accepted) {
|
|
168
|
+
completeOnce.run();
|
|
169
|
+
}
|
|
170
|
+
} catch (IllegalStateException ignored) {
|
|
171
|
+
// The JavaScript queue may already be unavailable during teardown.
|
|
172
|
+
completeOnce.run();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private boolean isCurrentRequest(int requestGeneration, boolean running) {
|
|
177
|
+
synchronized (stateLock) {
|
|
178
|
+
return generation == requestGeneration && requestedRunning == running;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private void markRequestDisabled(int requestGeneration) {
|
|
183
|
+
synchronized (stateLock) {
|
|
184
|
+
if (generation == requestGeneration) {
|
|
185
|
+
requestedRunning = false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private void disableRequestOnJsThread(int requestGeneration) {
|
|
191
|
+
markRequestDisabled(requestGeneration);
|
|
192
|
+
scheduler = null;
|
|
193
|
+
activeGeneration = 0;
|
|
194
|
+
lastFrameTimeNanos = 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private static Runnable once(final Runnable completion) {
|
|
198
|
+
final AtomicBoolean completed = new AtomicBoolean();
|
|
199
|
+
return new Runnable() {
|
|
200
|
+
@Override
|
|
201
|
+
public void run() {
|
|
202
|
+
if (completed.compareAndSet(false, true) && completion != null) {
|
|
203
|
+
completion.run();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private void handleFrame(long frameTimeNanos) {
|
|
210
|
+
if (!isCurrentRequest(activeGeneration, true) || scheduler == null) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (lastFrameTimeNanos != 0) {
|
|
215
|
+
long durationNanos = frameTimeNanos - lastFrameTimeNanos;
|
|
216
|
+
if (durationNanos > thresholdNanos) {
|
|
217
|
+
try {
|
|
218
|
+
// Take a detection-time snapshot before handing the event to the SDK.
|
|
219
|
+
reporter.reportLongTask(durationNanos, new HashMap<>(bridgeContext));
|
|
220
|
+
} catch (RuntimeException ignored) {
|
|
221
|
+
// Reporting must not interrupt the JavaScript frame callback loop.
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
lastFrameTimeNanos = frameTimeNanos;
|
|
226
|
+
|
|
227
|
+
if (isCurrentRequest(activeGeneration, true) && scheduler != null) {
|
|
228
|
+
try {
|
|
229
|
+
scheduler.postFrameCallback();
|
|
230
|
+
} catch (IllegalStateException ignored) {
|
|
231
|
+
disableRequestOnJsThread(activeGeneration);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private static final class ChoreographerSchedulerFactory implements FrameSchedulerFactory {
|
|
237
|
+
@Override
|
|
238
|
+
public FrameScheduler create(FrameCallback callback) {
|
|
239
|
+
return new ChoreographerScheduler(Choreographer.getInstance(), callback);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private static final class ChoreographerScheduler implements FrameScheduler {
|
|
244
|
+
private final Choreographer choreographer;
|
|
245
|
+
private final Choreographer.FrameCallback callback;
|
|
246
|
+
|
|
247
|
+
private ChoreographerScheduler(
|
|
248
|
+
Choreographer choreographer,
|
|
249
|
+
final FrameCallback callback
|
|
250
|
+
) {
|
|
251
|
+
this.choreographer = choreographer;
|
|
252
|
+
this.callback = new Choreographer.FrameCallback() {
|
|
253
|
+
@Override
|
|
254
|
+
public void doFrame(long frameTimeNanos) {
|
|
255
|
+
callback.doFrame(frameTimeNanos);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@Override
|
|
261
|
+
public void postFrameCallback() {
|
|
262
|
+
choreographer.postFrameCallback(callback);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@Override
|
|
266
|
+
public void removeFrameCallback() {
|
|
267
|
+
choreographer.removeFrameCallback(callback);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private static final class NativeLongTaskReporter implements Reporter {
|
|
272
|
+
@Override
|
|
273
|
+
public void reportLongTask(long durationNanos, Map<String, Object> property) {
|
|
274
|
+
FTRUMGlobalManager.get().addLongTask(
|
|
275
|
+
"",
|
|
276
|
+
durationNanos,
|
|
277
|
+
new HashMap<>(property)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package com.ft.sdk.reactnative;
|
|
2
|
+
|
|
3
|
+
final class ViewTiming {
|
|
4
|
+
private ViewTiming() {}
|
|
5
|
+
|
|
6
|
+
static long durationNanoseconds(double value) {
|
|
7
|
+
return (!Double.isNaN(value) && !Double.isInfinite(value)) && value > 0 && value < 0x1p63 ? (long) value : 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static long epochNanoseconds(double milliseconds) {
|
|
11
|
+
if ((Double.isNaN(milliseconds) || Double.isInfinite(milliseconds)) || milliseconds <= 0 || milliseconds >= 0x1p63 / 1000000.0) return 0;
|
|
12
|
+
long whole = (long) milliseconds;
|
|
13
|
+
long fraction = (long) ((milliseconds - whole) * 1000000.0);
|
|
14
|
+
if (whole > Long.MAX_VALUE / 1000000 || fraction > Long.MAX_VALUE - whole * 1000000) return 0;
|
|
15
|
+
return whole * 1000000 + fraction;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
package com.ft.sdk.reactnative;
|
|
2
|
+
|
|
3
|
+
import com.ft.sdk.FTVisitorIdConfig;
|
|
4
|
+
import java.net.URI;
|
|
5
|
+
import java.util.List;
|
|
6
|
+
import java.util.Map;
|
|
7
|
+
import java.util.regex.Pattern;
|
|
8
|
+
|
|
9
|
+
final class VisitorIdConfigMapper {
|
|
10
|
+
private VisitorIdConfigMapper() {}
|
|
11
|
+
|
|
12
|
+
static FTVisitorIdConfig fromMap(Object value) {
|
|
13
|
+
if (value == null) return null;
|
|
14
|
+
if (!(value instanceof Map)) throw new IllegalArgumentException("visitorIdConfig must be an object");
|
|
15
|
+
Map<?, ?> map = (Map<?, ?>) value;
|
|
16
|
+
if (!(map.get("header") instanceof String)) throw new IllegalArgumentException("Visitor ID header is required");
|
|
17
|
+
FTVisitorIdConfig config = new FTVisitorIdConfig().setHeader((String) map.get("header"));
|
|
18
|
+
if (map.containsKey("ttlHours")) {
|
|
19
|
+
if (!(map.get("ttlHours") instanceof Number)) throw new IllegalArgumentException("Visitor ID TTL must be numeric");
|
|
20
|
+
double hours = ((Number) map.get("ttlHours")).doubleValue();
|
|
21
|
+
double milliseconds = hours * 3600000;
|
|
22
|
+
if (Double.isNaN(hours) || Double.isInfinite(hours) || milliseconds < 1 || milliseconds >= Long.MAX_VALUE) {
|
|
23
|
+
throw new IllegalArgumentException("Visitor ID TTL is out of range");
|
|
24
|
+
}
|
|
25
|
+
config.setTtlHours(hours);
|
|
26
|
+
}
|
|
27
|
+
int rules = 0;
|
|
28
|
+
for (String key : new String[]{"urlPrefixes", "regularExpressions"}) {
|
|
29
|
+
Object values = map.get(key);
|
|
30
|
+
if (values == null && !map.containsKey(key)) continue;
|
|
31
|
+
if (!(values instanceof List)) throw new IllegalArgumentException(key + " must be an array");
|
|
32
|
+
for (Object item : (List<?>) values) {
|
|
33
|
+
if (!(item instanceof String) || ((String) item).isEmpty()) throw new IllegalArgumentException("Invalid Visitor ID rule");
|
|
34
|
+
String rule = (String) item;
|
|
35
|
+
if ("urlPrefixes".equals(key)) {
|
|
36
|
+
URI uri = URI.create(rule);
|
|
37
|
+
if (uri.getHost() == null || !("http".equalsIgnoreCase(uri.getScheme()) || "https".equalsIgnoreCase(uri.getScheme()))) {
|
|
38
|
+
throw new IllegalArgumentException("Visitor ID prefix must be an HTTP(S) URL");
|
|
39
|
+
}
|
|
40
|
+
config.addMatch(rule);
|
|
41
|
+
} else {
|
|
42
|
+
config.addMatch(Pattern.compile(rule));
|
|
43
|
+
}
|
|
44
|
+
rules++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (rules == 0) throw new IllegalArgumentException("Visitor ID requires URL matching rules");
|
|
48
|
+
return config;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
package com.ft.sdk.reactnative.utils;
|
|
2
2
|
|
|
3
3
|
import java.util.regex.Pattern;
|
|
4
|
-
import java.util.regex.Matcher;
|
|
5
4
|
|
|
6
5
|
public class ReactNativeUtils {
|
|
7
6
|
|
|
8
|
-
private static final Pattern
|
|
9
|
-
|
|
10
|
-
Pattern.
|
|
11
|
-
|
|
7
|
+
private static final Pattern RN_DEV_INNER_URL_PATTERN = Pattern.compile(
|
|
8
|
+
"^(?:https?|wss?)://(?:(?:10|172|192)(?:\\.[0-9]+){3}|localhost|127\\.0\\.0\\.1|\\[::1\\]):808[0-9]/(?:hot|symbolicate|message|inspector|status|assets|logs|debugger-proxy)(?:[/?#].*)?$",
|
|
9
|
+
Pattern.CASE_INSENSITIVE
|
|
10
|
+
);
|
|
12
11
|
|
|
13
12
|
private ReactNativeUtils() {
|
|
14
13
|
// Private constructor to prevent instantiation
|
|
@@ -48,12 +47,6 @@ public class ReactNativeUtils {
|
|
|
48
47
|
* @return
|
|
49
48
|
*/
|
|
50
49
|
public static boolean isReactNativeDevUrl(String url) {
|
|
51
|
-
|
|
52
|
-
Matcher matcher = pattern.matcher(url);
|
|
53
|
-
if (matcher.matches()) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return false;
|
|
50
|
+
return url != null && RN_DEV_INNER_URL_PATTERN.matcher(url).matches();
|
|
58
51
|
}
|
|
59
52
|
}
|
|
@@ -39,6 +39,11 @@ public class FTMobileModule extends NativeFTMobileReactNativeSpec {
|
|
|
39
39
|
impl.bindRUMUserData(userId, userName, userEmail, extra, promise);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
@ReactMethod
|
|
43
|
+
public void resetVisitorId(Promise promise) {
|
|
44
|
+
impl.resetVisitorId(promise);
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
@ReactMethod
|
|
43
48
|
public void unbindRUMUserData(Promise promise) {
|
|
44
49
|
impl.unbindRUMUserData(promise);
|
|
@@ -81,23 +86,27 @@ public class FTMobileModule extends NativeFTMobileReactNativeSpec {
|
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
@ReactMethod
|
|
89
|
+
@Override
|
|
84
90
|
public void updateRemoteConfig(Promise promise) {
|
|
85
|
-
impl.updateRemoteConfig(promise);
|
|
91
|
+
impl.updateRemoteConfig(promise);
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
@ReactMethod
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
@Override
|
|
96
|
+
public void updateRemoteConfigWithMiniUpdateInterval(double interval, @Nullable ReadableArray rules, Promise promise) {
|
|
97
|
+
impl.updateRemoteConfigWithMiniUpdateInterval((int) interval, rules, promise);
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
@ReactMethod
|
|
101
|
+
@Override
|
|
94
102
|
public void addListener(String eventName) {
|
|
95
|
-
|
|
103
|
+
// Required by NativeEventEmitter; Android does not need listener bookkeeping.
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
@ReactMethod
|
|
107
|
+
@Override
|
|
99
108
|
public void removeListeners(double count) {
|
|
100
|
-
|
|
109
|
+
// Required by NativeEventEmitter; Android does not need listener bookkeeping.
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
}
|
|
@@ -9,10 +9,11 @@ import com.facebook.react.bridge.ReadableMap;
|
|
|
9
9
|
|
|
10
10
|
public class FTRUMModule extends NativeFTReactNativeRUMSpec {
|
|
11
11
|
|
|
12
|
-
private final FTRUMImpl impl
|
|
12
|
+
private final FTRUMImpl impl;
|
|
13
13
|
|
|
14
14
|
public FTRUMModule(ReactApplicationContext reactContext) {
|
|
15
15
|
super(reactContext);
|
|
16
|
+
impl = new FTRUMImpl(reactContext);
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
@Override
|
|
@@ -21,6 +22,30 @@ public class FTRUMModule extends NativeFTReactNativeRUMSpec {
|
|
|
21
22
|
impl.setConfig(context, promise);
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
@Override
|
|
26
|
+
@ReactMethod
|
|
27
|
+
public void stopLongTaskTracking(Promise promise) {
|
|
28
|
+
impl.stopLongTaskTracking(promise);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
33
|
+
public boolean setLongTaskContext(ReadableMap context) {
|
|
34
|
+
return impl.setLongTaskContext(context);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Override
|
|
38
|
+
public void invalidate() {
|
|
39
|
+
impl.destroy();
|
|
40
|
+
super.invalidate();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Override
|
|
44
|
+
public void onCatalystInstanceDestroy() {
|
|
45
|
+
impl.destroy();
|
|
46
|
+
super.onCatalystInstanceDestroy();
|
|
47
|
+
}
|
|
48
|
+
|
|
24
49
|
@Override
|
|
25
50
|
@ReactMethod
|
|
26
51
|
public void startAction(String actionName, String actionType, ReadableMap map, Promise promise) {
|
|
@@ -33,6 +58,24 @@ public class FTRUMModule extends NativeFTReactNativeRUMSpec {
|
|
|
33
58
|
impl.addAction(actionName, actionType, map, promise);
|
|
34
59
|
}
|
|
35
60
|
|
|
61
|
+
@Override
|
|
62
|
+
@ReactMethod
|
|
63
|
+
public void onCreateViewWithLoadStart(String viewName, double duration, double loadStartEpochMs, Promise promise) {
|
|
64
|
+
impl.onCreateViewWithLoadStart(viewName, duration, loadStartEpochMs, promise);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Override
|
|
68
|
+
@ReactMethod
|
|
69
|
+
public void startViewWithTrackingType(String viewName, ReadableMap map, double trackingType, Promise promise) {
|
|
70
|
+
impl.startViewWithTrackingType(viewName, map, trackingType, promise);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Override
|
|
74
|
+
@ReactMethod
|
|
75
|
+
public void updateViewLoadingTime(double duration, Promise promise) {
|
|
76
|
+
impl.updateViewLoadingTime(duration, promise);
|
|
77
|
+
}
|
|
78
|
+
|
|
36
79
|
@Override
|
|
37
80
|
@ReactMethod
|
|
38
81
|
public void onCreateView(String viewName, double duration, Promise promise) {
|
|
@@ -4,6 +4,7 @@ import com.facebook.react.bridge.Promise;
|
|
|
4
4
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
5
5
|
import com.facebook.react.bridge.ReactMethod;
|
|
6
6
|
import com.facebook.react.bridge.ReadableMap;
|
|
7
|
+
import com.facebook.react.bridge.WritableMap;
|
|
7
8
|
|
|
8
9
|
public class FTTraceModule extends NativeFTReactNativeTraceSpec {
|
|
9
10
|
private final FTTraceImpl impl = new FTTraceImpl();
|
|
@@ -29,4 +30,17 @@ public class FTTraceModule extends NativeFTReactNativeTraceSpec {
|
|
|
29
30
|
public void getTraceHeaderFields(String url, String key, Promise promise) {
|
|
30
31
|
impl.getTraceHeaderFields(url, key, promise);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
@Override
|
|
35
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
36
|
+
public WritableMap getTraceHeaderFieldsSync(String url, String key) {
|
|
37
|
+
return impl.getTraceHeaderFieldsSync(url, key);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Override
|
|
41
|
+
@ReactMethod
|
|
42
|
+
public void cancelWebSocketTrace(String key, Promise promise) {
|
|
43
|
+
// JS WebSocket observation is iOS-only; Android owns its native lifecycle.
|
|
44
|
+
promise.resolve(null);
|
|
45
|
+
}
|
|
32
46
|
}
|
|
@@ -38,6 +38,11 @@ public class FTMobileModule extends ReactContextBaseJavaModule {
|
|
|
38
38
|
impl.bindRUMUserData(userId, userName, userEmail, extra, promise);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
@ReactMethod
|
|
42
|
+
public void resetVisitorId(Promise promise) {
|
|
43
|
+
impl.resetVisitorId(promise);
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
@ReactMethod
|
|
42
47
|
public void unbindRUMUserData(Promise promise) {
|
|
43
48
|
impl.unbindRUMUserData(promise);
|
|
@@ -85,12 +90,12 @@ public class FTMobileModule extends ReactContextBaseJavaModule {
|
|
|
85
90
|
|
|
86
91
|
@ReactMethod
|
|
87
92
|
public void addListener(String eventName) {
|
|
88
|
-
|
|
93
|
+
// Required by NativeEventEmitter; Android does not need listener bookkeeping.
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
@ReactMethod
|
|
92
97
|
public void removeListeners(double count) {
|
|
93
|
-
|
|
98
|
+
// Required by NativeEventEmitter; Android does not need listener bookkeeping.
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
@NonNull
|
|
@@ -7,10 +7,11 @@ import com.facebook.react.bridge.ReactMethod;
|
|
|
7
7
|
import com.facebook.react.bridge.ReadableMap;
|
|
8
8
|
|
|
9
9
|
public class FTRUMModule extends ReactContextBaseJavaModule {
|
|
10
|
-
private final FTRUMImpl impl
|
|
10
|
+
private final FTRUMImpl impl;
|
|
11
11
|
|
|
12
12
|
public FTRUMModule(ReactApplicationContext reactContext) {
|
|
13
13
|
super(reactContext);
|
|
14
|
+
impl = new FTRUMImpl(reactContext);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
@Override
|
|
@@ -23,6 +24,28 @@ public class FTRUMModule extends ReactContextBaseJavaModule {
|
|
|
23
24
|
impl.setConfig(context, promise);
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
@ReactMethod
|
|
28
|
+
public void stopLongTaskTracking(Promise promise) {
|
|
29
|
+
impl.stopLongTaskTracking(promise);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
33
|
+
public boolean setLongTaskContext(ReadableMap context) {
|
|
34
|
+
return impl.setLongTaskContext(context);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Override
|
|
38
|
+
public void invalidate() {
|
|
39
|
+
impl.destroy();
|
|
40
|
+
super.invalidate();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Override
|
|
44
|
+
public void onCatalystInstanceDestroy() {
|
|
45
|
+
impl.destroy();
|
|
46
|
+
super.onCatalystInstanceDestroy();
|
|
47
|
+
}
|
|
48
|
+
|
|
26
49
|
@ReactMethod
|
|
27
50
|
public void startAction(String actionName, String actionType, ReadableMap map, Promise promise) {
|
|
28
51
|
impl.startAction(actionName, actionType, map, promise);
|
|
@@ -33,6 +56,21 @@ public class FTRUMModule extends ReactContextBaseJavaModule {
|
|
|
33
56
|
impl.addAction(actionName, actionType, map, promise);
|
|
34
57
|
}
|
|
35
58
|
|
|
59
|
+
@ReactMethod
|
|
60
|
+
public void onCreateViewWithLoadStart(String viewName, double duration, double loadStartEpochMs, Promise promise) {
|
|
61
|
+
impl.onCreateViewWithLoadStart(viewName, duration, loadStartEpochMs, promise);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@ReactMethod
|
|
65
|
+
public void startViewWithTrackingType(String viewName, ReadableMap map, double trackingType, Promise promise) {
|
|
66
|
+
impl.startViewWithTrackingType(viewName, map, trackingType, promise);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@ReactMethod
|
|
70
|
+
public void updateViewLoadingTime(double duration, Promise promise) {
|
|
71
|
+
impl.updateViewLoadingTime(duration, promise);
|
|
72
|
+
}
|
|
73
|
+
|
|
36
74
|
@ReactMethod
|
|
37
75
|
public void onCreateView(String viewName, Double duration, Promise promise) {
|
|
38
76
|
impl.onCreateView(viewName, duration, promise);
|
|
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
|
|
5
5
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
6
6
|
import com.facebook.react.bridge.ReactMethod;
|
|
7
7
|
import com.facebook.react.bridge.ReadableMap;
|
|
8
|
+
import com.facebook.react.bridge.WritableMap;
|
|
8
9
|
|
|
9
10
|
public class FTTraceModule extends ReactContextBaseJavaModule {
|
|
10
11
|
private final FTTraceImpl impl = new FTTraceImpl();
|
|
@@ -27,4 +28,15 @@ public class FTTraceModule extends ReactContextBaseJavaModule {
|
|
|
27
28
|
public void getTraceHeaderFields(String url, String key, Promise promise) {
|
|
28
29
|
impl.getTraceHeaderFields(url, key, promise);
|
|
29
30
|
}
|
|
31
|
+
|
|
32
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
33
|
+
public WritableMap getTraceHeaderFieldsSync(String url, String key) {
|
|
34
|
+
return impl.getTraceHeaderFieldsSync(url, key);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@ReactMethod
|
|
38
|
+
public void cancelWebSocketTrace(String key, Promise promise) {
|
|
39
|
+
// JS WebSocket observation is iOS-only; Android owns its native lifecycle.
|
|
40
|
+
promise.resolve(null);
|
|
41
|
+
}
|
|
30
42
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package com.ft.sdk.reactnative;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
4
|
+
|
|
5
|
+
final class ReactJsThreadExecutor implements JsLongTaskMonitor.Executor {
|
|
6
|
+
private final ReactApplicationContext reactContext;
|
|
7
|
+
|
|
8
|
+
ReactJsThreadExecutor(ReactApplicationContext reactContext) {
|
|
9
|
+
this.reactContext = reactContext;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Override
|
|
13
|
+
public boolean runOnJsThread(Runnable runnable) {
|
|
14
|
+
try {
|
|
15
|
+
return reactContext.runOnJSQueueThread(runnable);
|
|
16
|
+
} catch (AssertionError ignored) {
|
|
17
|
+
// ReactContext asserts if its JS queue has not been initialized yet.
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|