@npaw/forge-react-native 9.6.1

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.
Files changed (74) hide show
  1. package/LICENSE +10 -0
  2. package/NPAWForgeReactNative.podspec +44 -0
  3. package/README.md +51 -0
  4. package/android/build.gradle +77 -0
  5. package/android/src/main/AndroidManifest.xml +14 -0
  6. package/android/src/main/java/com/npaw/forge/reactnative/ForgeDtoMapper.java +295 -0
  7. package/android/src/main/java/com/npaw/forge/reactnative/ForgeInstanceRegistry.java +61 -0
  8. package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgeModule.java +735 -0
  9. package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgePackage.java +53 -0
  10. package/android/src/test/java/com/npaw/forge/reactnative/ForgeDtoMapperTest.java +133 -0
  11. package/ios/ForgeApple/ForgeKernelFFI.xcframework/Info.plist +82 -0
  12. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/ForgeKernelFFI.h +17 -0
  13. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/forge_kernelFFI.h +873 -0
  14. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/module.modulemap +7 -0
  15. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/libForgeKernelFFI.a +0 -0
  16. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/ForgeKernelFFI.h +17 -0
  17. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/forge_kernelFFI.h +873 -0
  18. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap +7 -0
  19. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/libForgeKernelFFI.a +0 -0
  20. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/ForgeKernelFFI.h +17 -0
  21. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/forge_kernelFFI.h +873 -0
  22. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/module.modulemap +7 -0
  23. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/libForgeKernelFFI.a +0 -0
  24. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/ForgeKernelFFI.h +17 -0
  25. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/forge_kernelFFI.h +873 -0
  26. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/module.modulemap +7 -0
  27. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/libForgeKernelFFI.a +0 -0
  28. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernel.swift +3931 -0
  29. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.h +873 -0
  30. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.modulemap +7 -0
  31. package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAccessLogFacts.swift +152 -0
  32. package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAdapter.swift +877 -0
  33. package/ios/ForgeApple/Sources/NPAWForgeCapture/AttributionCapture.swift +158 -0
  34. package/ios/ForgeApple/Sources/NPAWForgeCapture/CapturePlanGate.swift +189 -0
  35. package/ios/ForgeApple/Sources/NPAWForgeCapture/CrashCapture.swift +120 -0
  36. package/ios/ForgeApple/Sources/NPAWForgeCapture/ForgeAutomaticCapture.swift +639 -0
  37. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsCapture.swift +1176 -0
  38. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsEngine.swift +674 -0
  39. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsMarkers.swift +105 -0
  40. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsSwiftUI.swift +284 -0
  41. package/ios/ForgeApple/Sources/NPAWForgeCapture/SwiftUICapture.swift +44 -0
  42. package/ios/ForgeApple/Sources/NPAWForgeCore/AdAdapter.swift +199 -0
  43. package/ios/ForgeApple/Sources/NPAWForgeCore/AdSemantics.swift +136 -0
  44. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeApple.swift +596 -0
  45. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeCapturePlan.swift +297 -0
  46. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeClock.swift +29 -0
  47. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeLogging.swift +109 -0
  48. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeOutcome.swift +89 -0
  49. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeRuntime.swift +291 -0
  50. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeStorage.swift +70 -0
  51. package/ios/ForgeApple/Sources/NPAWForgeCore/ManualAdReporter.swift +180 -0
  52. package/ios/ForgeApple/Sources/NPAWForgeCore/NPAWForge.swift +453 -0
  53. package/ios/ForgeApple/Sources/NPAWForgeCore/NativeUniFFIRuntime.swift +594 -0
  54. package/ios/ForgeApple/Sources/NPAWForgeCore/NetworkRequestRecord.swift +191 -0
  55. package/ios/ForgeApple/Sources/NPAWForgeCore/ObjectiveCWrappers.swift +191 -0
  56. package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerAdapter.swift +299 -0
  57. package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerSemantics.swift +266 -0
  58. package/ios/ForgeApple/Sources/NPAWForgeCore/PrivacySanitizer.swift +59 -0
  59. package/ios/ForgeApple/Sources/NPAWForgeCore/VideoAdapter.swift +478 -0
  60. package/ios/ForgeApple/Sources/NPAWForgeCore/VideoOptions.swift +431 -0
  61. package/ios/ForgeApple/Sources/NPAWForgeNetworkCapture/ForgeNetworkCapture.swift +116 -0
  62. package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/NPAWForgeNetworkSwizzler.m +293 -0
  63. package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/include/NPAWForgeNetworkSwizzler.h +33 -0
  64. package/ios/ForgeModuleImpl.swift +1131 -0
  65. package/ios/NPAWForgeTurboModule.mm +399 -0
  66. package/package.json +33 -0
  67. package/src/ForgeAdReporter.ts +101 -0
  68. package/src/ForgeVideo.ts +169 -0
  69. package/src/NPAWForge.ts +177 -0
  70. package/src/capture.ts +192 -0
  71. package/src/events.ts +40 -0
  72. package/src/index.ts +37 -0
  73. package/src/specs/NativeNPAWForge.ts +168 -0
  74. package/src/types.ts +114 -0
@@ -0,0 +1,735 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Core Forge TurboModule for React Native Android - full facade surface over the Kotlin NPAWForge facade through its Java-facing API (sessions, identity, raw/business, network, diagnostics, video fire*, ad reporters, kernel event feed). Java on purpose: javac ignores Kotlin metadata, so the Kotlin-2.3-built Forge AAR imposes no Kotlin floor on host apps.
13
+ */
14
+
15
+ package com.npaw.forge.reactnative;
16
+
17
+ import android.app.Application;
18
+ import android.content.Context;
19
+ import androidx.annotation.Nullable;
20
+ import com.facebook.react.bridge.Promise;
21
+ import com.facebook.react.bridge.ReactApplicationContext;
22
+ import com.facebook.react.bridge.ReadableMap;
23
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
24
+ import com.npaw.forge.android.NPAWForge;
25
+ import com.npaw.forge.android.core.ForgeEventSubscription;
26
+ import com.npaw.forge.android.core.ForgeOutcome;
27
+ import com.npaw.forge.android.core.ForgeRecordedEvent;
28
+ import com.npaw.forge.android.video.ManualAdReporter;
29
+ import com.npaw.forge.android.video.VideoAdapter;
30
+ import com.npaw.forge.android.video.VideoOptions;
31
+ import java.util.Map;
32
+ import java.util.concurrent.ConcurrentHashMap;
33
+ import java.util.concurrent.atomic.AtomicInteger;
34
+
35
+ public class NPAWForgeModule extends NativeNPAWForgeSpec {
36
+
37
+ public static final String NAME = "NPAWForgeRN";
38
+ public static final String FORGE_EVENT_CHANNEL = "NPAWForgeEvent";
39
+ private static final String E_UNKNOWN_INSTANCE = "E_FORGE_UNKNOWN_INSTANCE";
40
+ private static final String E_UNKNOWN_REPORTER = "E_FORGE_UNKNOWN_REPORTER";
41
+ private static final String E_BAD_ARGUMENT = "E_FORGE_BAD_ARGUMENT";
42
+
43
+ private interface ForgeCall {
44
+ ForgeOutcome apply(NPAWForge forge);
45
+ }
46
+
47
+ private interface VideoCall {
48
+ ForgeOutcome apply(VideoAdapter video);
49
+ }
50
+
51
+ private interface AdCall {
52
+ ForgeOutcome apply(ManualAdReporter reporter);
53
+ }
54
+
55
+ private final ConcurrentHashMap<String, ForgeEventSubscription> eventSubscriptions =
56
+ new ConcurrentHashMap<>();
57
+ private final ConcurrentHashMap<Integer, ManualAdReporter> adReporters = new ConcurrentHashMap<>();
58
+ private final AtomicInteger nextInstanceId = new AtomicInteger(1);
59
+ private final AtomicInteger nextReporterId = new AtomicInteger(1);
60
+ private final AtomicInteger listenerCount = new AtomicInteger(0);
61
+
62
+ public NPAWForgeModule(ReactApplicationContext reactContext) {
63
+ super(reactContext);
64
+ }
65
+
66
+ @Override
67
+ public String getName() {
68
+ return NAME;
69
+ }
70
+
71
+ // ---- lifecycle ----
72
+
73
+ @Override
74
+ public void create(String accountCode, @Nullable ReadableMap config, Promise promise) {
75
+ try {
76
+ if (accountCode.trim().isEmpty()) {
77
+ promise.reject(E_BAD_ARGUMENT, "accountCode must not be blank");
78
+ return;
79
+ }
80
+ Context appContext = getReactApplicationContext().getApplicationContext();
81
+ NPAWForge.Builder builder =
82
+ appContext instanceof Application
83
+ ? new NPAWForge.Builder((Application) appContext, accountCode)
84
+ : new NPAWForge.Builder(accountCode);
85
+ ForgeDtoMapper.applyConfig(builder, config);
86
+ NPAWForge forge = builder.build();
87
+ String instanceId = "forge-" + nextInstanceId.getAndIncrement();
88
+ ForgeInstanceRegistry.register(instanceId, forge);
89
+ if (listenerCount.get() > 0) {
90
+ subscribeInstanceEvents(instanceId, forge);
91
+ }
92
+ promise.resolve(instanceId);
93
+ } catch (Throwable t) {
94
+ promise.reject(E_BAD_ARGUMENT, "Failed to create NPAWForge: " + t.getMessage(), t);
95
+ }
96
+ }
97
+
98
+ @Override
99
+ public void destroy(String instanceId, Promise promise) {
100
+ NPAWForge forge = ForgeInstanceRegistry.remove(instanceId);
101
+ if (forge == null) {
102
+ promise.reject(E_UNKNOWN_INSTANCE, "No NPAWForge instance '" + instanceId + "'");
103
+ return;
104
+ }
105
+ ForgeEventSubscription subscription = eventSubscriptions.remove(instanceId);
106
+ if (subscription != null) {
107
+ subscription.cancel();
108
+ }
109
+ promise.resolve(ForgeDtoMapper.outcomeToMap(forge.destroy()));
110
+ }
111
+
112
+ @Override
113
+ public void setLogLevel(String instanceId, String level, Promise promise) {
114
+ withForge(instanceId, promise, forge -> forge.setLogLevel(level));
115
+ }
116
+
117
+ // ---- app session ----
118
+
119
+ @Override
120
+ public void startSession(String instanceId, @Nullable ReadableMap metadata, Promise promise) {
121
+ withForge(instanceId, promise, forge -> forge.startSession(ForgeDtoMapper.toObjectMap(metadata)));
122
+ }
123
+
124
+ @Override
125
+ public void endSession(String instanceId, @Nullable ReadableMap metadata, Promise promise) {
126
+ withForge(instanceId, promise, forge -> forge.endSession(ForgeDtoMapper.toObjectMap(metadata)));
127
+ }
128
+
129
+ @Override
130
+ public void trackSessionNavigation(String instanceId, @Nullable ReadableMap metadata, Promise promise) {
131
+ withForge(
132
+ instanceId, promise, forge -> forge.trackSessionNavigation(ForgeDtoMapper.toObjectMap(metadata)));
133
+ }
134
+
135
+ @Override
136
+ public void trackSessionEvent(
137
+ String instanceId, String name, @Nullable ReadableMap metadata, Promise promise) {
138
+ withForge(
139
+ instanceId, promise, forge -> forge.trackSessionEvent(name, ForgeDtoMapper.toObjectMap(metadata)));
140
+ }
141
+
142
+ @Override
143
+ public void trackSessionEventStop(
144
+ String instanceId, String name, @Nullable ReadableMap metadata, Promise promise) {
145
+ withForge(
146
+ instanceId,
147
+ promise,
148
+ forge -> forge.trackSessionEventStop(name, ForgeDtoMapper.toObjectMap(metadata)));
149
+ }
150
+
151
+ @Override
152
+ public void trackSessionError(
153
+ String instanceId,
154
+ String errorCode,
155
+ String errorMessage,
156
+ @Nullable ReadableMap metadata,
157
+ Promise promise) {
158
+ withForge(
159
+ instanceId,
160
+ promise,
161
+ forge -> forge.trackSessionError(errorCode, errorMessage, ForgeDtoMapper.toObjectMap(metadata)));
162
+ }
163
+
164
+ // ---- identity ----
165
+
166
+ @Override
167
+ public void loginSuccess(String instanceId, String userId, @Nullable ReadableMap options, Promise promise) {
168
+ withForge(instanceId, promise, forge -> forge.loginSuccess(userId, ForgeDtoMapper.toObjectMap(options)));
169
+ }
170
+
171
+ @Override
172
+ public void loginFailed(String instanceId, @Nullable ReadableMap options, Promise promise) {
173
+ withForge(instanceId, promise, forge -> forge.loginFailed(ForgeDtoMapper.toObjectMap(options)));
174
+ }
175
+
176
+ @Override
177
+ public void logout(String instanceId, @Nullable ReadableMap options, Promise promise) {
178
+ withForge(instanceId, promise, forge -> forge.logout(ForgeDtoMapper.toObjectMap(options)));
179
+ }
180
+
181
+ @Override
182
+ public void profileCreated(
183
+ String instanceId, String profileId, @Nullable ReadableMap options, Promise promise) {
184
+ withForge(
185
+ instanceId, promise, forge -> forge.profileCreated(profileId, ForgeDtoMapper.toObjectMap(options)));
186
+ }
187
+
188
+ @Override
189
+ public void profileSelected(
190
+ String instanceId, String profileId, @Nullable ReadableMap options, Promise promise) {
191
+ withForge(
192
+ instanceId, promise, forge -> forge.profileSelected(profileId, ForgeDtoMapper.toObjectMap(options)));
193
+ }
194
+
195
+ @Override
196
+ public void profileDeleted(String instanceId, String profileId, Promise promise) {
197
+ withForge(instanceId, promise, forge -> forge.profileDeleted(profileId));
198
+ }
199
+
200
+ // ---- raw / business ----
201
+
202
+ @Override
203
+ public void trackRawEvent(String instanceId, String name, @Nullable ReadableMap params, Promise promise) {
204
+ withForge(instanceId, promise, forge -> forge.trackRawEvent(name, ForgeDtoMapper.toObjectMap(params)));
205
+ }
206
+
207
+ @Override
208
+ public void trackSearch(String instanceId, String query, double resultCount, Promise promise) {
209
+ withForge(
210
+ instanceId,
211
+ promise,
212
+ forge -> forge.trackSearch(query, resultCount < 0 ? null : (int) resultCount));
213
+ }
214
+
215
+ @Override
216
+ public void trackSearchResultClick(
217
+ String instanceId, String query, @Nullable ReadableMap options, Promise promise) {
218
+ withForge(
219
+ instanceId,
220
+ promise,
221
+ forge -> forge.trackSearchResultClick(query, ForgeDtoMapper.toObjectMap(options)));
222
+ }
223
+
224
+ @Override
225
+ public void trackBusinessEvent(
226
+ String instanceId, String name, @Nullable ReadableMap attributes, Promise promise) {
227
+ withForge(
228
+ instanceId,
229
+ promise,
230
+ forge -> forge.trackBusinessEvent(name, ForgeDtoMapper.toObjectMap(attributes)));
231
+ }
232
+
233
+ @Override
234
+ public void trackAttribution(String instanceId, @Nullable ReadableMap options, Promise promise) {
235
+ withForge(instanceId, promise, forge -> forge.trackAttribution(ForgeDtoMapper.toObjectMap(options)));
236
+ }
237
+
238
+ // ---- network ----
239
+
240
+ @Override
241
+ public void trackNetworkRequest(String instanceId, ReadableMap record, Promise promise) {
242
+ withForge(
243
+ instanceId,
244
+ promise,
245
+ forge -> forge.trackNetworkRequest(ForgeDtoMapper.toNetworkRequestRecord(record)));
246
+ }
247
+
248
+ @Override
249
+ public void installNetworkCapture(String instanceId, @Nullable ReadableMap options, Promise promise) {
250
+ NPAWForge forge = resolveOrReject(instanceId, promise);
251
+ if (forge == null) {
252
+ return;
253
+ }
254
+ try {
255
+ com.npaw.forge.android.networkcapture.ForgeNetworkCapture.INSTANCE.install(
256
+ forge, new com.npaw.forge.android.networkcapture.NetworkCaptureConfiguration(true));
257
+ // createClientBuilder(), not createClient(): the latter consults the
258
+ // installed factory and would recurse straight back into this lambda.
259
+ com.facebook.react.modules.network.OkHttpClientProvider.setOkHttpClientFactory(
260
+ () ->
261
+ com.npaw.forge.android.networkcapture.OkHttp3Instrumentation.INSTANCE
262
+ .instrumentOkHttpClient(
263
+ com.facebook.react.modules.network.OkHttpClientProvider.createClientBuilder()
264
+ .build()));
265
+ promise.resolve(ForgeDtoMapper.outcomeToMap(ForgeOutcome.Accepted.INSTANCE));
266
+ } catch (Throwable t) {
267
+ promise.reject(E_BAD_ARGUMENT, "installNetworkCapture failed: " + t.getMessage(), t);
268
+ }
269
+ }
270
+
271
+ // ---- diagnostics ----
272
+
273
+ @Override
274
+ public void snapshotEvents(String instanceId, Promise promise) {
275
+ NPAWForge forge = resolveOrReject(instanceId, promise);
276
+ if (forge == null) {
277
+ return;
278
+ }
279
+ try {
280
+ promise.resolve(ForgeDtoMapper.snapshotToMap(instanceId, forge.snapshotEvents()));
281
+ } catch (Throwable t) {
282
+ promise.reject(E_BAD_ARGUMENT, "Forge call failed: " + t.getMessage(), t);
283
+ }
284
+ }
285
+
286
+ // ---- video ----
287
+
288
+ @Override
289
+ public void createVideo(
290
+ String instanceId, String identifier, @Nullable ReadableMap options, Promise promise) {
291
+ NPAWForge forge = resolveOrReject(instanceId, promise);
292
+ if (forge == null) {
293
+ return;
294
+ }
295
+ try {
296
+ VideoOptions videoOptions =
297
+ new VideoOptions.Builder().metadata(ForgeDtoMapper.toStringMap(options)).build();
298
+ forge.videoBuilder().setIdentifier(identifier).setOptions(videoOptions).build();
299
+ promise.resolve(ForgeDtoMapper.outcomeToMap(ForgeOutcome.Accepted.INSTANCE));
300
+ } catch (Throwable t) {
301
+ promise.reject(E_BAD_ARGUMENT, "createVideo failed: " + t.getMessage(), t);
302
+ }
303
+ }
304
+
305
+ @Override
306
+ public void videoDestroy(String instanceId, String identifier, Promise promise) {
307
+ withVideo(instanceId, identifier, promise, VideoAdapter::destroy);
308
+ }
309
+
310
+ @Override
311
+ public void videoFireInit(
312
+ String instanceId, String identifier, @Nullable ReadableMap params, Promise promise) {
313
+ withVideo(instanceId, identifier, promise, video -> video.fireInit(ForgeDtoMapper.toStringMap(params)));
314
+ }
315
+
316
+ @Override
317
+ public void videoFireStart(
318
+ String instanceId, String identifier, @Nullable ReadableMap params, boolean forceInit, Promise promise) {
319
+ withVideo(
320
+ instanceId,
321
+ identifier,
322
+ promise,
323
+ video -> video.fireStart(ForgeDtoMapper.toStringMap(params), forceInit ? Boolean.TRUE : null));
324
+ }
325
+
326
+ @Override
327
+ public void videoFireJoin(
328
+ String instanceId, String identifier, @Nullable ReadableMap params, Promise promise) {
329
+ withVideo(instanceId, identifier, promise, video -> video.fireJoin(ForgeDtoMapper.toStringMap(params)));
330
+ }
331
+
332
+ @Override
333
+ public void videoFirePause(
334
+ String instanceId, String identifier, @Nullable ReadableMap params, Promise promise) {
335
+ withVideo(instanceId, identifier, promise, video -> video.firePause(ForgeDtoMapper.toStringMap(params)));
336
+ }
337
+
338
+ @Override
339
+ public void videoFireResume(
340
+ String instanceId, String identifier, @Nullable ReadableMap params, Promise promise) {
341
+ withVideo(instanceId, identifier, promise, video -> video.fireResume(ForgeDtoMapper.toStringMap(params)));
342
+ }
343
+
344
+ @Override
345
+ public void videoFireSeekBegin(
346
+ String instanceId,
347
+ String identifier,
348
+ @Nullable ReadableMap params,
349
+ boolean convertFromBuffer,
350
+ Promise promise) {
351
+ withVideo(
352
+ instanceId,
353
+ identifier,
354
+ promise,
355
+ video ->
356
+ video.fireSeekBegin(
357
+ ForgeDtoMapper.toStringMap(params), convertFromBuffer ? Boolean.TRUE : null, 0L));
358
+ }
359
+
360
+ @Override
361
+ public void videoFireSeekEnd(
362
+ String instanceId, String identifier, @Nullable ReadableMap params, Promise promise) {
363
+ withVideo(instanceId, identifier, promise, video -> video.fireSeekEnd(ForgeDtoMapper.toStringMap(params)));
364
+ }
365
+
366
+ @Override
367
+ public void videoFireBufferBegin(
368
+ String instanceId,
369
+ String identifier,
370
+ @Nullable ReadableMap params,
371
+ boolean convertFromSeek,
372
+ Promise promise) {
373
+ withVideo(
374
+ instanceId,
375
+ identifier,
376
+ promise,
377
+ video ->
378
+ video.fireBufferBegin(
379
+ ForgeDtoMapper.toStringMap(params), convertFromSeek ? Boolean.TRUE : null, 0L));
380
+ }
381
+
382
+ @Override
383
+ public void videoFireBufferEnd(
384
+ String instanceId, String identifier, @Nullable ReadableMap params, Promise promise) {
385
+ withVideo(
386
+ instanceId, identifier, promise, video -> video.fireBufferEnd(ForgeDtoMapper.toStringMap(params)));
387
+ }
388
+
389
+ @Override
390
+ public void videoFireStop(
391
+ String instanceId, String identifier, @Nullable ReadableMap params, Promise promise) {
392
+ withVideo(instanceId, identifier, promise, video -> video.fireStop(ForgeDtoMapper.toStringMap(params)));
393
+ }
394
+
395
+ @Override
396
+ public void videoFireError(
397
+ String instanceId,
398
+ String identifier,
399
+ String errorCode,
400
+ String errorMessage,
401
+ String errorMetadata,
402
+ Promise promise) {
403
+ withVideo(
404
+ instanceId,
405
+ identifier,
406
+ promise,
407
+ video ->
408
+ video.fireError(
409
+ ForgeDtoMapper.emptyToNull(errorCode),
410
+ ForgeDtoMapper.emptyToNull(errorMessage),
411
+ ForgeDtoMapper.emptyToNull(errorMetadata),
412
+ 0L));
413
+ }
414
+
415
+ @Override
416
+ public void videoFireFatalError(
417
+ String instanceId,
418
+ String identifier,
419
+ String errorCode,
420
+ String errorMessage,
421
+ String errorMetadata,
422
+ Promise promise) {
423
+ withVideo(
424
+ instanceId,
425
+ identifier,
426
+ promise,
427
+ video ->
428
+ video.fireFatalError(
429
+ ForgeDtoMapper.emptyToNull(errorCode),
430
+ ForgeDtoMapper.emptyToNull(errorMessage),
431
+ ForgeDtoMapper.emptyToNull(errorMetadata),
432
+ 0L));
433
+ }
434
+
435
+ @Override
436
+ public void videoFireDestroy(String instanceId, String identifier, Promise promise) {
437
+ withVideo(instanceId, identifier, promise, video -> video.fireDestroy(0L));
438
+ }
439
+
440
+ @Override
441
+ public void videoFireEvent(
442
+ String instanceId,
443
+ String identifier,
444
+ String eventName,
445
+ @Nullable ReadableMap dimensions,
446
+ @Nullable ReadableMap values,
447
+ @Nullable ReadableMap topLevelDimensions,
448
+ boolean hasEndDatetime,
449
+ Promise promise) {
450
+ withVideo(
451
+ instanceId,
452
+ identifier,
453
+ promise,
454
+ video ->
455
+ video.fireEvent(
456
+ eventName,
457
+ ForgeDtoMapper.toStringMap(dimensions),
458
+ ForgeDtoMapper.toDoubleMap(values),
459
+ ForgeDtoMapper.toStringMap(topLevelDimensions),
460
+ hasEndDatetime,
461
+ 0L));
462
+ }
463
+
464
+ @Override
465
+ public void videoFireEventEnd(
466
+ String instanceId,
467
+ String identifier,
468
+ String eventName,
469
+ @Nullable ReadableMap dimensions,
470
+ @Nullable ReadableMap values,
471
+ @Nullable ReadableMap topLevelDimensions,
472
+ Promise promise) {
473
+ withVideo(
474
+ instanceId,
475
+ identifier,
476
+ promise,
477
+ video ->
478
+ video.fireEventEnd(
479
+ eventName,
480
+ ForgeDtoMapper.toStringMap(dimensions),
481
+ ForgeDtoMapper.toDoubleMap(values),
482
+ ForgeDtoMapper.toStringMap(topLevelDimensions),
483
+ 0L));
484
+ }
485
+
486
+ @Override
487
+ public void videoFireMetadataUpdate(String instanceId, String identifier, Promise promise) {
488
+ withVideo(instanceId, identifier, promise, VideoAdapter::fireMetadataUpdate);
489
+ }
490
+
491
+ // ---- ads ----
492
+
493
+ @Override
494
+ public void createAdReporter(String instanceId, String identifier, String adapterVersion, Promise promise) {
495
+ if (resolveOrReject(instanceId, promise) == null) {
496
+ return;
497
+ }
498
+ VideoAdapter video = ForgeInstanceRegistry.resolveVideoAdapter(instanceId, identifier);
499
+ if (video == null) {
500
+ promise.reject(E_BAD_ARGUMENT, "No video '" + identifier + "' on instance '" + instanceId + "'");
501
+ return;
502
+ }
503
+ try {
504
+ ManualAdReporter reporter = video.createAdReporter(ForgeDtoMapper.emptyToNull(adapterVersion));
505
+ int reporterId = nextReporterId.getAndIncrement();
506
+ adReporters.put(reporterId, reporter);
507
+ promise.resolve(reporterId);
508
+ } catch (Throwable t) {
509
+ promise.reject(E_BAD_ARGUMENT, "createAdReporter failed: " + t.getMessage(), t);
510
+ }
511
+ }
512
+
513
+ @Override
514
+ public void releaseAdReporter(double reporterId, Promise promise) {
515
+ ManualAdReporter reporter = adReporters.remove((int) reporterId);
516
+ if (reporter == null) {
517
+ promise.reject(E_UNKNOWN_REPORTER, "No ad reporter " + (int) reporterId);
518
+ return;
519
+ }
520
+ promise.resolve(ForgeDtoMapper.outcomeToMap(ForgeOutcome.Accepted.INSTANCE));
521
+ }
522
+
523
+ @Override
524
+ public void adFireManifest(double reporterId, @Nullable ReadableMap params, Promise promise) {
525
+ withReporter(reporterId, promise, reporter -> reporter.fireManifest(ForgeDtoMapper.toStringMap(params)));
526
+ }
527
+
528
+ @Override
529
+ public void adFireBreakStart(double reporterId, @Nullable ReadableMap params, Promise promise) {
530
+ withReporter(
531
+ reporterId, promise, reporter -> reporter.fireBreakStart(ForgeDtoMapper.toStringMap(params)));
532
+ }
533
+
534
+ @Override
535
+ public void adFireAdInit(double reporterId, @Nullable ReadableMap params, Promise promise) {
536
+ withReporter(reporterId, promise, reporter -> reporter.fireAdInit(ForgeDtoMapper.toStringMap(params)));
537
+ }
538
+
539
+ @Override
540
+ public void adFireStart(double reporterId, @Nullable ReadableMap params, Promise promise) {
541
+ withReporter(reporterId, promise, reporter -> reporter.fireStart(ForgeDtoMapper.toStringMap(params)));
542
+ }
543
+
544
+ @Override
545
+ public void adFireJoin(double reporterId, @Nullable ReadableMap params, Promise promise) {
546
+ withReporter(reporterId, promise, reporter -> reporter.fireJoin(ForgeDtoMapper.toStringMap(params)));
547
+ }
548
+
549
+ @Override
550
+ public void adFirePause(double reporterId, @Nullable ReadableMap params, Promise promise) {
551
+ withReporter(reporterId, promise, reporter -> reporter.firePause(ForgeDtoMapper.toStringMap(params)));
552
+ }
553
+
554
+ @Override
555
+ public void adFireResume(double reporterId, @Nullable ReadableMap params, Promise promise) {
556
+ withReporter(reporterId, promise, reporter -> reporter.fireResume(ForgeDtoMapper.toStringMap(params)));
557
+ }
558
+
559
+ @Override
560
+ public void adFireBufferBegin(double reporterId, @Nullable ReadableMap params, Promise promise) {
561
+ withReporter(
562
+ reporterId, promise, reporter -> reporter.fireBufferBegin(ForgeDtoMapper.toStringMap(params)));
563
+ }
564
+
565
+ @Override
566
+ public void adFireBufferEnd(double reporterId, @Nullable ReadableMap params, Promise promise) {
567
+ withReporter(
568
+ reporterId, promise, reporter -> reporter.fireBufferEnd(ForgeDtoMapper.toStringMap(params)));
569
+ }
570
+
571
+ @Override
572
+ public void adFireQuartile(double reporterId, double quartile, Promise promise) {
573
+ withReporter(
574
+ reporterId, promise, reporter -> reporter.fireQuartile(quartile < 0 ? null : (int) quartile, 0L));
575
+ }
576
+
577
+ @Override
578
+ public void adFireClick(double reporterId, @Nullable ReadableMap params, Promise promise) {
579
+ withReporter(reporterId, promise, reporter -> reporter.fireClick(ForgeDtoMapper.toStringMap(params)));
580
+ }
581
+
582
+ @Override
583
+ public void adFireSkip(double reporterId, Promise promise) {
584
+ withReporter(reporterId, promise, reporter -> reporter.fireSkip(0L));
585
+ }
586
+
587
+ @Override
588
+ public void adFireStop(double reporterId, @Nullable ReadableMap params, Promise promise) {
589
+ withReporter(reporterId, promise, reporter -> reporter.fireStop(ForgeDtoMapper.toStringMap(params)));
590
+ }
591
+
592
+ @Override
593
+ public void adFireError(
594
+ double reporterId, String errorCode, String errorMessage, String errorMetadata, Promise promise) {
595
+ withReporter(
596
+ reporterId,
597
+ promise,
598
+ reporter ->
599
+ reporter.fireError(
600
+ ForgeDtoMapper.emptyToNull(errorCode),
601
+ ForgeDtoMapper.emptyToNull(errorMessage),
602
+ ForgeDtoMapper.emptyToNull(errorMetadata),
603
+ 0L));
604
+ }
605
+
606
+ @Override
607
+ public void adFireFatalError(
608
+ double reporterId, String errorCode, String errorMessage, String errorMetadata, Promise promise) {
609
+ withReporter(
610
+ reporterId,
611
+ promise,
612
+ reporter ->
613
+ reporter.fireFatalError(
614
+ ForgeDtoMapper.emptyToNull(errorCode),
615
+ ForgeDtoMapper.emptyToNull(errorMessage),
616
+ ForgeDtoMapper.emptyToNull(errorMetadata),
617
+ 0L));
618
+ }
619
+
620
+ @Override
621
+ public void adFireBreakStop(double reporterId, @Nullable ReadableMap params, Promise promise) {
622
+ withReporter(
623
+ reporterId, promise, reporter -> reporter.fireBreakStop(ForgeDtoMapper.toStringMap(params)));
624
+ }
625
+
626
+ @Override
627
+ public void adFireDestroy(double reporterId, Promise promise) {
628
+ withReporter(reporterId, promise, reporter -> reporter.fireDestroy(0L));
629
+ }
630
+
631
+ // ---- event feed ----
632
+
633
+ @Override
634
+ public void addListener(String eventName) {
635
+ if (listenerCount.getAndIncrement() == 0) {
636
+ for (Map.Entry<String, NPAWForge> entry : ForgeInstanceRegistry.snapshot().entrySet()) {
637
+ subscribeInstanceEvents(entry.getKey(), entry.getValue());
638
+ }
639
+ }
640
+ }
641
+
642
+ @Override
643
+ public void removeListeners(double count) {
644
+ if (listenerCount.addAndGet(-(int) count) <= 0) {
645
+ listenerCount.set(0);
646
+ cancelAllSubscriptions();
647
+ }
648
+ }
649
+
650
+ @Override
651
+ public void invalidate() {
652
+ cancelAllSubscriptions();
653
+ adReporters.clear();
654
+ for (NPAWForge forge : ForgeInstanceRegistry.clear()) {
655
+ forge.destroy();
656
+ }
657
+ super.invalidate();
658
+ }
659
+
660
+ // ---- internals ----
661
+
662
+ private void cancelAllSubscriptions() {
663
+ for (ForgeEventSubscription subscription : eventSubscriptions.values()) {
664
+ subscription.cancel();
665
+ }
666
+ eventSubscriptions.clear();
667
+ }
668
+
669
+ private void subscribeInstanceEvents(final String instanceId, NPAWForge forge) {
670
+ eventSubscriptions.computeIfAbsent(
671
+ instanceId, key -> forge.subscribeEvents((event, outcome) -> emitForgeEvent(instanceId, event)));
672
+ }
673
+
674
+ private void emitForgeEvent(String instanceId, ForgeRecordedEvent event) {
675
+ ReactApplicationContext context = getReactApplicationContext();
676
+ if (!context.hasActiveReactInstance()) {
677
+ return;
678
+ }
679
+ context
680
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
681
+ .emit(FORGE_EVENT_CHANNEL, ForgeDtoMapper.recordedEventToMap(instanceId, event));
682
+ }
683
+
684
+ @Nullable
685
+ private NPAWForge resolveOrReject(String instanceId, Promise promise) {
686
+ NPAWForge forge = ForgeInstanceRegistry.resolve(instanceId);
687
+ if (forge == null) {
688
+ promise.reject(E_UNKNOWN_INSTANCE, "No NPAWForge instance '" + instanceId + "'");
689
+ }
690
+ return forge;
691
+ }
692
+
693
+ private void withForge(String instanceId, Promise promise, ForgeCall call) {
694
+ NPAWForge forge = resolveOrReject(instanceId, promise);
695
+ if (forge == null) {
696
+ return;
697
+ }
698
+ try {
699
+ promise.resolve(ForgeDtoMapper.outcomeToMap(call.apply(forge)));
700
+ } catch (Throwable t) {
701
+ promise.reject(E_BAD_ARGUMENT, "Forge call failed: " + t.getMessage(), t);
702
+ }
703
+ }
704
+
705
+ private void withVideo(String instanceId, String identifier, Promise promise, VideoCall call) {
706
+ if (resolveOrReject(instanceId, promise) == null) {
707
+ return;
708
+ }
709
+ VideoAdapter video = ForgeInstanceRegistry.resolveVideoAdapter(instanceId, identifier);
710
+ if (video == null) {
711
+ // The video may have been legitimately destroyed; report facade state
712
+ // semantics instead of a programmer error.
713
+ promise.resolve(ForgeDtoMapper.outcomeToMap(ForgeOutcome.IgnoredInvalidState.INSTANCE));
714
+ return;
715
+ }
716
+ try {
717
+ promise.resolve(ForgeDtoMapper.outcomeToMap(call.apply(video)));
718
+ } catch (Throwable t) {
719
+ promise.reject(E_BAD_ARGUMENT, "Forge video call failed: " + t.getMessage(), t);
720
+ }
721
+ }
722
+
723
+ private void withReporter(double reporterId, Promise promise, AdCall call) {
724
+ ManualAdReporter reporter = adReporters.get((int) reporterId);
725
+ if (reporter == null) {
726
+ promise.reject(E_UNKNOWN_REPORTER, "No ad reporter " + (int) reporterId);
727
+ return;
728
+ }
729
+ try {
730
+ promise.resolve(ForgeDtoMapper.outcomeToMap(call.apply(reporter)));
731
+ } catch (Throwable t) {
732
+ promise.reject(E_BAD_ARGUMENT, "Forge ad call failed: " + t.getMessage(), t);
733
+ }
734
+ }
735
+ }