react-native-insider 8.1.0 → 9.0.0-nh

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 (48) hide show
  1. package/README.md +89 -660
  2. package/RNInsider.podspec +33 -12
  3. package/android/build.gradle +16 -7
  4. package/android/src/main/java/com/useinsider/react/RNInsiderAppFrameViewManager.java +253 -0
  5. package/android/src/main/java/com/useinsider/react/RNInsiderModule.java +238 -178
  6. package/android/src/main/java/com/useinsider/react/RNInsiderPackage.java +5 -1
  7. package/android/src/main/java/com/useinsider/react/RNUtils.java +111 -0
  8. package/babel.config.js +4 -1
  9. package/index.d.ts +196 -161
  10. package/index.js +60 -11
  11. package/ios/RNInsider/RNInsider.m +245 -341
  12. package/ios/RNInsider/RNInsiderAppFrameView.h +33 -0
  13. package/ios/RNInsider/RNInsiderAppFrameView.mm +315 -0
  14. package/ios/RNInsider/RNNotificationHandler.h +4 -1
  15. package/ios/RNInsider/RNNotificationHandler.m +16 -18
  16. package/ios/RNInsider/RNUtils.h +2 -0
  17. package/ios/RNInsider/RNUtils.m +79 -0
  18. package/package.json +43 -12
  19. package/src/EventHandler.js +17 -0
  20. package/src/InsiderAppFrame.d.ts +45 -0
  21. package/src/InsiderAppFrame.js +265 -0
  22. package/src/InsiderAppFrameLogic.d.ts +35 -0
  23. package/src/InsiderAppFrameLogic.js +192 -0
  24. package/src/InsiderAppFrameStatus.d.ts +22 -0
  25. package/src/InsiderAppFrameStatus.js +35 -0
  26. package/src/InsiderAppFramesError.d.ts +25 -0
  27. package/src/InsiderAppFramesError.js +77 -0
  28. package/src/InsiderEvent.js +1 -0
  29. package/src/InsiderEvents.d.ts +93 -0
  30. package/src/InsiderEvents.js +124 -0
  31. package/src/InsiderIdentifier.js +1 -1
  32. package/src/InsiderUser.d.ts +3 -2
  33. package/src/InsiderUser.js +27 -0
  34. package/src/Util.js +2 -1
  35. package/src/specs/InsiderAppFrameNativeComponent.ts +54 -0
  36. package/.orbit-output/config/MASTER.md +0 -129
  37. package/.orbit-output/config/architecture/API_DOCS.md +0 -342
  38. package/.orbit-output/config/architecture/ARCHITECTURE.md +0 -209
  39. package/.orbit-output/config/architecture/CODE-CATALOG.md +0 -172
  40. package/.orbit-output/config/architecture/project.yaml +0 -138
  41. package/.orbit-output/config/automation/AUTOMATION-SETUP.md +0 -50
  42. package/.orbit-output/config/enforcement/ENFORCEMENT-RULES.md +0 -41
  43. package/.orbit-output/config/enforcement/validate.sh +0 -49
  44. package/.orbit-output/skills/custom/academy-doc-generator.md +0 -598
  45. package/android/.settings/org.eclipse.buildship.core.prefs +0 -13
  46. package/android/src/test/java/com/useinsider/react/RNInsiderModuleTest.java +0 -169
  47. package/docs/app-cards/app-cards_new.md +0 -569
  48. package/ios/RNInsiderTests/RNInsiderTriggerPushProcessTests.m +0 -91
@@ -7,13 +7,16 @@ import android.os.Looper;
7
7
  import android.content.Intent;
8
8
  import android.net.Uri;
9
9
 
10
+ import com.facebook.react.bridge.Arguments;
10
11
  import com.facebook.react.bridge.Callback;
11
12
  import com.facebook.react.bridge.ReactApplicationContext;
13
+ import com.facebook.react.bridge.ReactContext;
12
14
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
13
15
  import com.facebook.react.bridge.ReactMethod;
14
16
  import com.facebook.react.bridge.ReadableArray;
15
17
  import com.facebook.react.bridge.ReadableMap;
16
18
  import com.facebook.react.bridge.WritableMap;
19
+ import com.facebook.react.bridge.WritableNativeMap;
17
20
  import com.facebook.react.modules.core.DeviceEventManagerModule;
18
21
  import com.google.firebase.messaging.RemoteMessage;
19
22
  import com.useinsider.insider.Insider;
@@ -31,10 +34,12 @@ import com.useinsider.insider.RecommendationEngine;
31
34
  import com.useinsider.insiderhybrid.InsiderHybrid;
32
35
  import com.useinsider.insiderhybrid.constants.InsiderHybridMethods;
33
36
  import com.useinsider.insider.InsiderIDListener;
37
+ import com.useinsider.insider.InsiderEventListener;
34
38
 
35
39
  import org.json.JSONArray;
36
40
  import org.json.JSONObject;
37
41
 
42
+ import java.lang.reflect.Method;
38
43
  import java.util.Date;
39
44
  import java.util.HashMap;
40
45
  import java.util.Map;
@@ -48,6 +53,11 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
48
53
 
49
54
  private InsiderIDListener insiderIDListener;
50
55
 
56
+ // Written on the NativeModules thread, read again from the posted main-looper Runnable
57
+ // whose identity check decides whether to register. volatile makes that read's visibility
58
+ // explicit rather than relying on the happens-before edge Handler.post happens to give us.
59
+ private volatile InsiderEventListener insiderEventListener;
60
+
51
61
  public RNInsiderModule(ReactApplicationContext reactContext) {
52
62
  super(reactContext);
53
63
  this.reactContext = reactContext;
@@ -58,40 +68,69 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
58
68
  return "RNInsider";
59
69
  }
60
70
 
61
- @ReactMethod
62
- public void setName(String name) {
71
+ private static final Method emitDeviceEventMethod = resolveEmitDeviceEvent();
72
+
73
+ private static Method resolveEmitDeviceEvent() {
63
74
  try {
64
- Insider.Instance.getCurrentUser().setName(name);
65
- } catch (Exception e) {
66
- Insider.Instance.putException(e);
75
+ return ReactContext.class.getMethod("emitDeviceEvent", String.class, Object.class);
76
+ } catch (NoSuchMethodException e) {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ private void emitDeviceEventCompat(String eventName, Object payload) {
82
+ ReactApplicationContext context = getReactApplicationContext();
83
+
84
+ if (emitDeviceEventMethod != null) {
85
+ try {
86
+ emitDeviceEventMethod.invoke(context, eventName, payload);
87
+ return;
88
+ } catch (Exception e) {
89
+ Insider.Instance.putException(e);
90
+ return;
91
+ }
92
+ }
93
+
94
+ DeviceEventManagerModule.RCTDeviceEventEmitter emitter =
95
+ context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
96
+
97
+ if (emitter != null) {
98
+ emitter.emit(eventName, payload);
67
99
  }
68
100
  }
69
101
 
70
102
  @ReactMethod
71
- public void init(final String partnerName, final String sdkVersion) {
103
+ public void setName(String name) {
104
+ Insider.Instance.getCurrentUser().setName(name);
105
+ }
106
+
107
+ @ReactMethod
108
+ public void init(final String partnerName, final String sdkVersion, final String appIdentifier) {
72
109
  try {
73
110
  if (Insider.Instance.isSDKInitialized()) {
74
111
  return;
75
112
  }
113
+ final String app = resolveAppIdentifier(appIdentifier);
76
114
  new Handler(Looper.getMainLooper()).post(new Runnable() {
77
115
  @Override
78
116
  public void run() {
79
117
  Insider.Instance.registerInsiderCallback(new InsiderCallback() {
80
- DeviceEventManagerModule.RCTDeviceEventEmitter emitter = getReactApplicationContext()
81
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
82
-
83
118
  @Override
84
119
  public void doAction(JSONObject jsonObject, InsiderCallbackType insiderCallbackType) {
85
120
  try {
86
121
  WritableMap callbackData = RNUtils.convertJSONObjectToMap(jsonObject);
87
- emitter.emit(insiderCallbackType.name(), callbackData);
122
+ emitDeviceEventCompat(insiderCallbackType.name(), callbackData);
88
123
  } catch (Exception e) {
89
124
  Insider.Instance.putException(e);
90
125
  }
91
126
  }
92
127
  });
93
128
 
94
- Insider.Instance.init(((Application) reactContext.getApplicationContext()), partnerName);
129
+ if (app != null) {
130
+ Insider.Instance.init(((Application) reactContext.getApplicationContext()), partnerName, app);
131
+ } else {
132
+ Insider.Instance.init(((Application) reactContext.getApplicationContext()), partnerName);
133
+ }
95
134
  Insider.Instance.setSDKType("react-native");
96
135
  Insider.Instance.setHybridSDKVersion(sdkVersion);
97
136
 
@@ -109,22 +148,18 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
109
148
  }
110
149
  }
111
150
 
151
+ private String resolveAppIdentifier(String app) {
152
+ return app != null && !app.isEmpty() ? app : null;
153
+ }
154
+
112
155
  @ReactMethod
113
156
  public void reinitWithPartnerName(String partnerName) {
114
- try {
115
- Insider.Instance.reinitWithPartnerName(partnerName);
116
- } catch (Exception e) {
117
- Insider.Instance.putException(e);
118
- }
157
+ Insider.Instance.reinitWithPartnerName(partnerName);
119
158
  }
120
159
 
121
160
  @ReactMethod
122
161
  public void setCustomEndpoint(String endpoint) {
123
- try {
124
- Insider.Instance.setCustomEndpoint(endpoint);
125
- } catch (Exception e) {
126
- Insider.Instance.putException(e);
127
- }
162
+ Insider.Instance.setCustomEndpoint(endpoint);
128
163
  }
129
164
 
130
165
  @ReactMethod
@@ -138,11 +173,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
138
173
 
139
174
  @ReactMethod
140
175
  public void setGender(int gender) {
141
- try {
142
- InsiderHybrid.setGender(gender);
143
- } catch (Exception e) {
144
- Insider.Instance.putException(e);
145
- }
176
+ InsiderHybrid.setGender(gender);
146
177
  }
147
178
 
148
179
  @ReactMethod
@@ -159,116 +190,73 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
159
190
 
160
191
  @ReactMethod
161
192
  public void setSurname(String surname) {
162
- try {
163
- Insider.Instance.getCurrentUser().setSurname(surname);
164
- } catch (Exception e) {
165
- Insider.Instance.putException(e);
166
- }
193
+ Insider.Instance.getCurrentUser().setSurname(surname);
167
194
  }
168
195
 
169
196
  @ReactMethod
170
197
  public void setAge(int age) {
171
- try {
172
- Insider.Instance.getCurrentUser().setAge(age);
173
- } catch (Exception e) {
174
- Insider.Instance.putException(e);
175
- }
198
+ Insider.Instance.getCurrentUser().setAge(age);
176
199
  }
177
200
 
178
201
  @ReactMethod
179
202
  public void setLanguage(String language) {
180
- try {
181
- Insider.Instance.getCurrentUser().setLanguage(language);
182
- } catch (Exception e) {
183
- Insider.Instance.putException(e);
184
- }
203
+ Insider.Instance.getCurrentUser().setLanguage(language);
185
204
  }
186
205
 
187
206
  @ReactMethod
188
207
  public void setLocale(String locale) {
189
- try {
190
- Insider.Instance.getCurrentUser().setLocale(locale);
191
- } catch (Exception e) {
192
- Insider.Instance.putException(e);
193
- }
208
+ Insider.Instance.getCurrentUser().setLocale(locale);
194
209
  }
195
210
 
196
211
  @ReactMethod
197
212
  public void setFacebookID(String facebookID) {
198
- try {
199
- Insider.Instance.getCurrentUser().setFacebookID(facebookID);
200
- } catch (Exception e) {
201
- Insider.Instance.putException(e);
202
- }
213
+ Insider.Instance.getCurrentUser().setFacebookID(facebookID);
203
214
  }
204
215
 
205
216
  @ReactMethod
206
217
  public void setTwitterID(String twitterID) {
207
- try {
208
- Insider.Instance.getCurrentUser().setTwitterID(twitterID);
209
- } catch (Exception e) {
210
- Insider.Instance.putException(e);
211
- }
218
+ Insider.Instance.getCurrentUser().setTwitterID(twitterID);
212
219
  }
213
220
 
214
221
  @ReactMethod
215
222
  public void setEmailOptin(boolean emailOptin) {
216
- try {
217
- Insider.Instance.getCurrentUser().setEmailOptin(emailOptin);
218
- } catch (Exception e) {
219
- Insider.Instance.putException(e);
220
- }
223
+ Insider.Instance.getCurrentUser().setEmailOptin(emailOptin);
221
224
  }
222
225
 
223
226
  @ReactMethod
224
227
  public void setEmail(String email) {
225
- try {
226
- Insider.Instance.getCurrentUser().setEmail(email);
227
- } catch (Exception e) {
228
- Insider.Instance.putException(e);
229
- }
228
+ Insider.Instance.getCurrentUser().setEmail(email);
230
229
  }
231
230
 
232
231
  @ReactMethod
233
232
  public void setSMSOptin(boolean smsOptin) {
234
- try {
235
- Insider.Instance.getCurrentUser().setSMSOptin(smsOptin);
236
- } catch (Exception e) {
237
- Insider.Instance.putException(e);
238
- }
233
+ Insider.Instance.getCurrentUser().setSMSOptin(smsOptin);
239
234
  }
240
235
 
241
236
  @ReactMethod
242
237
  public void setPhoneNumber(String phoneNumber) {
243
- try {
244
- Insider.Instance.getCurrentUser().setPhoneNumber(phoneNumber);
245
- } catch (Exception e) {
246
- Insider.Instance.putException(e);
247
- }
238
+ Insider.Instance.getCurrentUser().setPhoneNumber(phoneNumber);
248
239
  }
249
240
 
250
241
  @ReactMethod
251
242
  public void setPushOptin(boolean pushOptin) {
252
- try {
253
- Insider.Instance.getCurrentUser().setPushOptin(pushOptin);
254
- } catch (Exception e) {
255
- Insider.Instance.putException(e);
256
- }
243
+ Insider.Instance.getCurrentUser().setPushOptin(pushOptin);
257
244
  }
258
245
 
259
246
  @ReactMethod
260
247
  public void setLocationOptin(boolean locationOptin) {
261
- try {
262
- Insider.Instance.getCurrentUser().setLocationOptin(locationOptin);
263
- } catch (Exception e) {
264
- Insider.Instance.putException(e);
265
- }
248
+ Insider.Instance.getCurrentUser().setLocationOptin(locationOptin);
266
249
  }
267
250
 
268
251
  @ReactMethod
269
252
  public void setWhatsappOptin(boolean whatsappOptin) {
253
+ Insider.Instance.getCurrentUser().setWhatsappOptin(whatsappOptin);
254
+ }
255
+
256
+ @ReactMethod
257
+ public void setPromotionalOptin(boolean promotionalOptin) {
270
258
  try {
271
- Insider.Instance.getCurrentUser().setWhatsappOptin(whatsappOptin);
259
+ Insider.Instance.getCurrentUser().setPromotionalOptin(promotionalOptin);
272
260
  } catch (Exception e) {
273
261
  Insider.Instance.putException(e);
274
262
  }
@@ -276,13 +264,9 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
276
264
 
277
265
  @ReactMethod
278
266
  public void login(ReadableMap identifiers) {
279
- try {
280
- InsiderIdentifiers setIdentifiers = setIdentifiers(identifiers);
267
+ InsiderIdentifiers setIdentifiers = setIdentifiers(identifiers);
281
268
 
282
- Insider.Instance.getCurrentUser().login(setIdentifiers);
283
- } catch (Exception e) {
284
- Insider.Instance.putException(e);
285
- }
269
+ Insider.Instance.getCurrentUser().login(setIdentifiers);
286
270
  }
287
271
 
288
272
  @ReactMethod
@@ -302,6 +286,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
302
286
  });
303
287
  } catch (Exception e) {
304
288
  Insider.Instance.putException(e);
289
+ RNInsiderIDResult.invoke((String) null);
305
290
  }
306
291
  }
307
292
 
@@ -335,11 +320,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
335
320
 
336
321
  @ReactMethod
337
322
  public void logout() {
338
- try {
339
- Insider.Instance.getCurrentUser().logout();
340
- } catch (Exception e) {
341
- Insider.Instance.putException(e);
342
- }
323
+ Insider.Instance.getCurrentUser().logout();
343
324
  }
344
325
 
345
326
  @ReactMethod
@@ -363,6 +344,9 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
363
344
  }
364
345
  } catch (Exception e) {
365
346
  Insider.Instance.putException(e);
347
+ if (insiderIDResult != null) {
348
+ insiderIDResult.invoke((String) null);
349
+ }
366
350
  }
367
351
  }
368
352
 
@@ -381,38 +365,22 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
381
365
 
382
366
  @ReactMethod
383
367
  public void setCustomAttributeWithString(String key, String value) {
384
- try {
385
- Insider.Instance.getCurrentUser().setCustomAttributeWithString(key, value);
386
- } catch (Exception e) {
387
- Insider.Instance.putException(e);
388
- }
368
+ Insider.Instance.getCurrentUser().setCustomAttributeWithString(key, value);
389
369
  }
390
370
 
391
371
  @ReactMethod
392
372
  public void setCustomAttributeWithInt(String key, int value) {
393
- try {
394
- Insider.Instance.getCurrentUser().setCustomAttributeWithInt(key, value);
395
- } catch (Exception e) {
396
- Insider.Instance.putException(e);
397
- }
373
+ Insider.Instance.getCurrentUser().setCustomAttributeWithInt(key, value);
398
374
  }
399
375
 
400
376
  @ReactMethod
401
377
  public void setCustomAttributeWithDouble(String key, double value) {
402
- try {
403
- Insider.Instance.getCurrentUser().setCustomAttributeWithDouble(key, value);
404
- } catch (Exception e) {
405
- Insider.Instance.putException(e);
406
- }
378
+ Insider.Instance.getCurrentUser().setCustomAttributeWithDouble(key, value);
407
379
  }
408
380
 
409
381
  @ReactMethod
410
382
  public void setCustomAttributeWithBoolean(String key, boolean value) {
411
- try {
412
- Insider.Instance.getCurrentUser().setCustomAttributeWithBoolean(key, value);
413
- } catch (Exception e) {
414
- Insider.Instance.putException(e);
415
- }
383
+ Insider.Instance.getCurrentUser().setCustomAttributeWithBoolean(key, value);
416
384
  }
417
385
 
418
386
  @ReactMethod
@@ -438,11 +406,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
438
406
 
439
407
  @ReactMethod
440
408
  public void unsetCustomAttribute(String key) {
441
- try {
442
- Insider.Instance.getCurrentUser().unsetCustomAttribute(key);
443
- } catch (Exception e) {
444
- Insider.Instance.putException(e);
445
- }
409
+ Insider.Instance.getCurrentUser().unsetCustomAttribute(key);
446
410
  }
447
411
 
448
412
  @ReactMethod
@@ -450,22 +414,14 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
450
414
  new Handler(Looper.getMainLooper()).post(new Runnable() {
451
415
  @Override
452
416
  public void run() {
453
- try {
454
- Insider.Instance.setGDPRConsent(gdprConsent);
455
- } catch (Exception e) {
456
- Insider.Instance.putException(e);
457
- }
417
+ Insider.Instance.setGDPRConsent(gdprConsent);
458
418
  }
459
419
  });
460
420
  }
461
421
 
462
422
  @ReactMethod
463
423
  public void setMobileAppAccess(final boolean mobileAppAccess) {
464
- try {
465
- Insider.Instance.setMobileAppAccess(mobileAppAccess);
466
- } catch (Exception e) {
467
- Insider.Instance.putException(e);
468
- }
424
+ Insider.Instance.setMobileAppAccess(mobileAppAccess);
469
425
  }
470
426
 
471
427
  @ReactMethod
@@ -580,11 +536,11 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
580
536
 
581
537
  @ReactMethod
582
538
  public void getMessageCenterData(int limit, String startDate, String endDate, final Callback messageCenterCallback) {
583
- // React Native's bridge does not support long type directly, so we use String to pass epoch time.
584
- long startDateEpoch = Long.parseLong(startDate);
585
- long endDateEpoch = Long.parseLong(endDate);
586
-
587
539
  try {
540
+ // React Native's bridge does not support long type directly, so we use String to pass epoch time.
541
+ long startDateEpoch = Long.parseLong(startDate);
542
+ long endDateEpoch = Long.parseLong(endDate);
543
+
588
544
  InsiderHybrid.getMessageCenterData(limit, startDateEpoch, endDateEpoch, new MessageCenterData() {
589
545
  @Override
590
546
  public void loadMessageCenterData(JSONArray messageCenterData) {
@@ -597,15 +553,16 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
597
553
  });
598
554
  } catch (Exception e) {
599
555
  Insider.Instance.putException(e);
556
+ messageCenterCallback.invoke("[]");
600
557
  }
601
558
  }
602
559
 
603
560
  @ReactMethod
604
561
  public void getMessageCenterDataWithIdentifiers(int limit, String startDate, String endDate, ReadableMap identifiers, final Callback messageCenterCallback) {
605
- long startDateEpoch = Long.parseLong(startDate);
606
- long endDateEpoch = Long.parseLong(endDate);
607
-
608
562
  try {
563
+ long startDateEpoch = Long.parseLong(startDate);
564
+ long endDateEpoch = Long.parseLong(endDate);
565
+
609
566
  Insider.Instance.getMessageCenterData(
610
567
  limit,
611
568
  new Date(startDateEpoch),
@@ -623,6 +580,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
623
580
  });
624
581
  } catch (Exception e) {
625
582
  Insider.Instance.putException(e);
583
+ messageCenterCallback.invoke("[]");
626
584
  }
627
585
  }
628
586
 
@@ -643,6 +601,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
643
601
  });
644
602
  } catch (Exception e) {
645
603
  Insider.Instance.putException(e);
604
+ smartRecommendation.invoke(Arguments.createMap());
646
605
  }
647
606
  }
648
607
 
@@ -664,6 +623,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
664
623
  });
665
624
  } catch (Exception e) {
666
625
  Insider.Instance.putException(e);
626
+ callback.invoke(Arguments.createMap());
667
627
  }
668
628
  }
669
629
 
@@ -685,6 +645,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
685
645
  });
686
646
  } catch (Exception e) {
687
647
  Insider.Instance.putException(e);
648
+ callback.invoke(Arguments.createMap());
688
649
  }
689
650
  }
690
651
 
@@ -758,6 +719,15 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
758
719
  }
759
720
  }
760
721
 
722
+ @ReactMethod
723
+ public void markContentOptimizerAsSeen(String variableName) {
724
+ try {
725
+ Insider.Instance.markContentOptimizerAsSeen(variableName);
726
+ } catch (Exception e) {
727
+ Insider.Instance.putException(e);
728
+ }
729
+ }
730
+
761
731
 
762
732
  @ReactMethod
763
733
  public void visitHomePage(ReadableArray customParameters) {
@@ -863,11 +833,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
863
833
  new Handler(Looper.getMainLooper()).post(new Runnable() {
864
834
  @Override
865
835
  public void run() {
866
- try {
867
- Insider.Instance.startTrackingGeofence();
868
- } catch (Exception e) {
869
- Insider.Instance.putException(e);
870
- }
836
+ Insider.Instance.startTrackingGeofence();
871
837
  }
872
838
  });
873
839
  }
@@ -895,10 +861,6 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
895
861
  }
896
862
  String provider = Insider.Instance.getCurrentProvider(reactContext);
897
863
  switch (provider) {
898
- case "huawei":
899
- com.huawei.hms.push.RemoteMessage hmsRemoteMessage = new com.huawei.hms.push.RemoteMessage.Builder("insider").setData(remoteMessageStringMap).build();
900
- Insider.Instance.handleHMSNotification(reactContext, hmsRemoteMessage);
901
- break;
902
864
  case "other":
903
865
  case "google":
904
866
  RemoteMessage fcmRemoteMessage = new RemoteMessage.Builder("insider").setData(remoteMessageStringMap).build();
@@ -944,12 +906,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
944
906
 
945
907
  @ReactMethod
946
908
  public void putErrorLog(String exception) {
947
- try {
948
- Exception exceptionObject = new Exception(exception);
949
- Insider.Instance.putException(exceptionObject);
950
- } catch (Exception ignored) {
951
-
952
- }
909
+ Insider.Instance.putException(new Exception(exception));
953
910
  }
954
911
 
955
912
  @ReactMethod
@@ -966,11 +923,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
966
923
  new Handler(Looper.getMainLooper()).post(new Runnable() {
967
924
  @Override
968
925
  public void run() {
969
- try {
970
- Insider.Instance.enableLocationCollection(consentStatus);
971
- } catch (Exception e) {
972
- Insider.Instance.putException(e);
973
- }
926
+ Insider.Instance.enableLocationCollection(consentStatus);
974
927
  }
975
928
  });
976
929
  }
@@ -980,11 +933,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
980
933
  new Handler(Looper.getMainLooper()).post(new Runnable() {
981
934
  @Override
982
935
  public void run() {
983
- try {
984
- Insider.Instance.enableIpCollection(consentStatus);
985
- } catch (Exception e) {
986
- Insider.Instance.putException(e);
987
- }
936
+ Insider.Instance.enableIpCollection(consentStatus);
988
937
  }
989
938
  });
990
939
  }
@@ -994,11 +943,7 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
994
943
  new Handler(Looper.getMainLooper()).post(new Runnable() {
995
944
  @Override
996
945
  public void run() {
997
- try {
998
- Insider.Instance.enableCarrierCollection(consentStatus);
999
- } catch (Exception e) {
1000
- Insider.Instance.putException(e);
1001
- }
946
+ Insider.Instance.enableCarrierCollection(consentStatus);
1002
947
  }
1003
948
  });
1004
949
  }
@@ -1068,12 +1013,9 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
1068
1013
  @Override
1069
1014
  public void run() {
1070
1015
  insiderIDListener = new InsiderIDListener() {
1071
- DeviceEventManagerModule.RCTDeviceEventEmitter emitter = getReactApplicationContext()
1072
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
1073
-
1074
1016
  @Override
1075
1017
  public void onUpdated(String insiderID) {
1076
- emitter.emit("INSIDER_ID_LISTENER", insiderID);
1018
+ emitDeviceEventCompat("INSIDER_ID_LISTENER", insiderID);
1077
1019
  }
1078
1020
  };
1079
1021
 
@@ -1087,24 +1029,142 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
1087
1029
  }
1088
1030
  }
1089
1031
 
1032
+ /**
1033
+ * Registers a single {@link InsiderEventListener} that forwards every recorded event to
1034
+ * JS as {@code INSIDER_EVENT_RECORDED}. The JS layer fans out to its own subscribers, so
1035
+ * this bridge only ever needs one native observer — the null-guard makes repeat calls
1036
+ * idempotent.
1037
+ *
1038
+ * <p>Note the SDK delivers on its own background thread; only the REGISTRATION is hopped
1039
+ * onto the main looper (matching {@link #registerInsiderIDListener()}). The
1040
+ * {@code parameters} map handed to us is unmodifiable, so we only read from it.
1041
+ *
1042
+ * <p>{@code Insider.Instance.events()} returns a no-op instance before SDK init, so it is
1043
+ * resolved fresh here at registration time rather than cached in a field.
1044
+ */
1090
1045
  @ReactMethod
1091
- public void disableInAppMessages() {
1046
+ public void registerInsiderEventListener() {
1092
1047
  try {
1093
- Insider.Instance.disableInAppMessages();
1048
+ // The guard and the field assignment BOTH happen synchronously on the calling
1049
+ // thread. Deferring the assignment into the posted Runnable would let the guard
1050
+ // race unregisterInsiderEventListener(), which reads the field synchronously:
1051
+ // a subscribe-then-immediate-unsubscribe would see null, no-op, and then leave
1052
+ // the Runnable to register an observer JS believes is gone; a subscribe burst
1053
+ // would see null twice and addObserver() twice, double-delivering every event.
1054
+ // Only the SDK call itself is hopped onto the main looper.
1055
+ if (insiderEventListener != null) return;
1056
+
1057
+ final InsiderEventListener listener = new InsiderEventListener() {
1058
+ @Override
1059
+ public void onEventRecorded(String name, Map<String, Object> parameters, long timestamp) {
1060
+ try {
1061
+ WritableMap payload = new WritableNativeMap();
1062
+ payload.putString("name", name);
1063
+ payload.putMap("parameters", RNUtils.convertObjectMapToWritableMap(parameters));
1064
+ // Passed through in WHOLE SECONDS, matching the native SDK.
1065
+ // JS must do `new Date(timestamp * 1000)`.
1066
+ payload.putDouble("timestamp", (double) timestamp);
1067
+
1068
+ // THREADING (matches the note in iOS RNInsider.m — do not
1069
+ // re-litigate): this runs on the SDK's listener thread and
1070
+ // needs no manual hop. RCTDeviceEventEmitter.emit is a
1071
+ // JS-module proxy call; the bridge enqueues it onto the JS
1072
+ // thread itself, in order, so emitting from here is safe.
1073
+ //
1074
+ // The emitter is resolved per emit rather than cached: a dev
1075
+ // reload or Activity recreation replaces the catalyst instance,
1076
+ // and a cached proxy would then target a dead one and silently
1077
+ // stop delivering. emitDeviceEventCompat also covers bridgeless,
1078
+ // where getJSModule may return null.
1079
+ emitDeviceEventCompat("INSIDER_EVENT_RECORDED", payload);
1080
+ } catch (Exception e) {
1081
+ Insider.Instance.putException(e);
1082
+ }
1083
+ }
1084
+ };
1085
+
1086
+ insiderEventListener = listener;
1087
+
1088
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
1089
+ @Override
1090
+ public void run() {
1091
+ try {
1092
+ // An unregister may have landed while this was queued. Only register
1093
+ // if this listener is still the current one, so we never revive an
1094
+ // observer the JS side has already dropped.
1095
+ if (insiderEventListener != listener) return;
1096
+
1097
+ Insider.Instance.events().addObserver(listener);
1098
+ } catch (Exception e) {
1099
+ if (insiderEventListener == listener) insiderEventListener = null;
1100
+ Insider.Instance.putException(e);
1101
+ }
1102
+ }
1103
+ });
1094
1104
  } catch (Exception e) {
1095
1105
  Insider.Instance.putException(e);
1096
1106
  }
1097
1107
  }
1098
1108
 
1109
+ /**
1110
+ * Removes the observer registered by {@link #registerInsiderEventListener()}. Android
1111
+ * holds listeners STRONGLY, so this teardown is required to avoid leaking the listener
1112
+ * for the lifetime of the process.
1113
+ *
1114
+ * <p>The field is cleared synchronously so a register that is still queued sees the
1115
+ * identity change and cancels itself. Both methods post to the same looper, so the SDK
1116
+ * add/remove calls also stay in submission order.
1117
+ */
1099
1118
  @ReactMethod
1100
- public void enableInAppMessages() {
1119
+ public void unregisterInsiderEventListener() {
1101
1120
  try {
1102
- Insider.Instance.enableInAppMessages();
1121
+ if (insiderEventListener != null) {
1122
+ final InsiderEventListener listenerToRemove = insiderEventListener;
1123
+ insiderEventListener = null;
1124
+
1125
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
1126
+ @Override
1127
+ public void run() {
1128
+ try {
1129
+ Insider.Instance.events().removeObserver(listenerToRemove);
1130
+ } catch (Exception e) {
1131
+ Insider.Instance.putException(e);
1132
+ }
1133
+ }
1134
+ });
1135
+ }
1103
1136
  } catch (Exception e) {
1104
1137
  Insider.Instance.putException(e);
1105
1138
  }
1106
1139
  }
1107
1140
 
1141
+ /**
1142
+ * Tears the event observer down when the bridge itself goes away — a dev reload or an
1143
+ * Activity recreation destroys the catalyst instance without JS ever getting to call
1144
+ * {@link #unregisterInsiderEventListener()}.
1145
+ *
1146
+ * <p>This matters because Android holds observers STRONGLY: without this the listener
1147
+ * survives the bridge, keeps the dead {@code ReactApplicationContext} alive through it,
1148
+ * and every subsequent reload stacks another observer onto the SDK. The delegation keeps
1149
+ * the teardown sequence (synchronous field clear, then the main-looper removeObserver) in
1150
+ * one place rather than duplicating it here.
1151
+ */
1152
+ @Override
1153
+ public void invalidate() {
1154
+ unregisterInsiderEventListener();
1155
+ super.invalidate();
1156
+ }
1157
+
1158
+ @ReactMethod
1159
+ public void disableInAppMessages() {
1160
+ Insider.Instance.disableInAppMessages();
1161
+ }
1162
+
1163
+ @ReactMethod
1164
+ public void enableInAppMessages() {
1165
+ Insider.Instance.enableInAppMessages();
1166
+ }
1167
+
1108
1168
  @ReactMethod
1109
1169
  public void getAppCardsCampaigns(final Callback callback) {
1110
1170
  try {