@luciq/react-native 19.6.0 → 19.7.0

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.
@@ -2,6 +2,7 @@ package ai.luciq.reactlibrary;
2
2
 
3
3
  import static ai.luciq.apm.configuration.cp.APMFeature.APM_NETWORK_PLUGIN_INSTALLED;
4
4
  import static ai.luciq.apm.configuration.cp.APMFeature.CP_NATIVE_INTERCEPTION_ENABLED;
5
+ import static ai.luciq.reactlibrary.Constants.NET_TAG;
5
6
  import static ai.luciq.reactlibrary.utils.LuciqUtil.getMethod;
6
7
 
7
8
  import android.app.Application;
@@ -13,8 +14,6 @@ import android.os.Build;
13
14
  import android.util.Log;
14
15
  import android.view.View;
15
16
 
16
- import com.facebook.react.bridge.ReactApplicationContext;
17
-
18
17
  import androidx.annotation.NonNull;
19
18
  import androidx.annotation.UiThread;
20
19
 
@@ -32,22 +31,37 @@ import com.facebook.react.bridge.WritableNativeArray;
32
31
  import com.facebook.react.bridge.WritableNativeMap;
33
32
  import com.facebook.react.uimanager.UIManagerHelper;
34
33
  import com.facebook.react.uimanager.UIManagerModule;
34
+
35
+ import org.json.JSONException;
36
+ import org.json.JSONObject;
37
+ import org.json.JSONTokener;
38
+
39
+ import java.io.File;
40
+ import java.lang.reflect.Method;
41
+ import java.util.ArrayList;
42
+ import java.util.Arrays;
43
+ import java.util.HashMap;
44
+ import java.util.Iterator;
45
+ import java.util.List;
46
+ import java.util.Locale;
47
+ import java.util.Map;
48
+
49
+ import javax.annotation.Nullable;
50
+
35
51
  import ai.luciq.apm.InternalAPM;
36
- import ai.luciq.apm.configuration.cp.APMFeature;
37
52
  import ai.luciq.library.Feature;
53
+ import ai.luciq.library.IssueType;
54
+ import ai.luciq.library.LogLevel;
38
55
  import ai.luciq.library.Luciq;
39
56
  import ai.luciq.library.LuciqColorTheme;
40
57
  import ai.luciq.library.LuciqCustomTextPlaceHolder;
41
- import ai.luciq.library.IssueType;
42
- import ai.luciq.library.LogLevel;
43
58
  import ai.luciq.library.ReproConfigurations;
44
59
  import ai.luciq.library.core.InstabugCore;
60
+ import ai.luciq.library.featuresflags.model.LuciqFeatureFlag;
45
61
  import ai.luciq.library.internal.crossplatform.CoreFeature;
46
62
  import ai.luciq.library.internal.crossplatform.CoreFeaturesState;
47
63
  import ai.luciq.library.internal.crossplatform.FeaturesStateListener;
48
64
  import ai.luciq.library.internal.crossplatform.InternalCore;
49
- import ai.luciq.library.featuresflags.model.LuciqFeatureFlag;
50
- import ai.luciq.library.internal.crossplatform.InternalCore;
51
65
  import ai.luciq.library.internal.crossplatform.OnFeaturesUpdatedListener;
52
66
  import ai.luciq.library.internal.module.LuciqLocale;
53
67
  import ai.luciq.library.invocation.LuciqInvocationEvent;
@@ -56,29 +70,12 @@ import ai.luciq.library.model.LuciqTheme;
56
70
  import ai.luciq.library.model.NetworkLog;
57
71
  import ai.luciq.library.model.Report;
58
72
  import ai.luciq.library.ui.onboarding.WelcomeMessage;
59
- import ai.luciq.library.util.LuciqSDKLogger;
60
73
  import ai.luciq.reactlibrary.utils.ArrayUtil;
61
74
  import ai.luciq.reactlibrary.utils.EventEmitterModule;
75
+ import ai.luciq.reactlibrary.utils.LuciqRNLogger;
62
76
  import ai.luciq.reactlibrary.utils.MainThreadHandler;
63
-
64
77
  import ai.luciq.reactlibrary.utils.RNTouchedViewExtractor;
65
78
 
66
- import org.json.JSONException;
67
- import org.json.JSONObject;
68
- import org.json.JSONTokener;
69
-
70
- import java.io.File;
71
- import java.lang.reflect.Method;
72
- import java.util.ArrayList;
73
- import java.util.Arrays;
74
- import java.util.HashMap;
75
- import java.util.Iterator;
76
- import java.util.List;
77
- import java.util.Locale;
78
- import java.util.Map;
79
-
80
- import javax.annotation.Nullable;
81
-
82
79
 
83
80
  /**
84
81
  * The type Rn luciq reactnative module.
@@ -86,6 +83,7 @@ import javax.annotation.Nullable;
86
83
  public class RNLuciqReactnativeModule extends EventEmitterModule {
87
84
 
88
85
  private static final String TAG = "Luciq-RN-Core";
86
+ ;
89
87
 
90
88
  private LuciqCustomTextPlaceHolder placeHolders;
91
89
  private static Report currentReport;
@@ -123,6 +121,7 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
123
121
 
124
122
  /**
125
123
  * Enables or disables Luciq functionality.
124
+ *
126
125
  * @param isEnabled A boolean to enable/disable Luciq.
127
126
  */
128
127
  @ReactMethod
@@ -163,6 +162,9 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
163
162
 
164
163
 
165
164
  ) {
165
+ final int parsedLogLevel = ArgsRegistry.sdkLogLevels.getOrDefault(logLevel, LogLevel.ERROR);
166
+ LuciqRNLogger.setLevel(parsedLogLevel);
167
+ LuciqRNLogger.d(NET_TAG, "[init] Called — logLevel=" + logLevel + ", useNativeNetworkInterception=" + useNativeNetworkInterception + ", codePushVersion=" + codePushVersion + ", appVariant=" + appVariant);
166
168
  MainThreadHandler.runOnMainThread(new Runnable() {
167
169
  @Override
168
170
  public void run() {
@@ -171,7 +173,6 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
171
173
  final ArrayList<String> keys = ArrayUtil.parseReadableArrayOfStrings(invocationEventValues);
172
174
  final ArrayList<LuciqInvocationEvent> parsedInvocationEvents = ArgsRegistry.invocationEvents.getAll(keys);
173
175
  final LuciqInvocationEvent[] invocationEvents = parsedInvocationEvents.toArray(new LuciqInvocationEvent[0]);
174
- final int parsedLogLevel = ArgsRegistry.sdkLogLevels.getOrDefault(logLevel, LogLevel.ERROR);
175
176
 
176
177
  final Application application = (Application) reactContext.getApplicationContext();
177
178
 
@@ -179,7 +180,7 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
179
180
  .setInvocationEvents(invocationEvents)
180
181
  .setLogLevel(parsedLogLevel);
181
182
 
182
- if (map!=null&&map.hasKey("ignoreAndroidSecureFlag")) {
183
+ if (map != null && map.hasKey("ignoreAndroidSecureFlag")) {
183
184
  builder.ignoreFlagSecure(map.getBoolean("ignoreAndroidSecureFlag"));
184
185
  }
185
186
 
@@ -190,12 +191,12 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
190
191
  builder.setCodePushVersion(codePushVersion);
191
192
  }
192
193
  }
193
- if (appVariant != null) {
194
- builder.setAppVariant(appVariant);
195
- }
194
+ if (appVariant != null) {
195
+ builder.setAppVariant(appVariant);
196
+ }
196
197
 
197
- if(overAirVersion != null ) {
198
- if(Luciq.isBuilt()) {
198
+ if (overAirVersion != null) {
199
+ if (Luciq.isBuilt()) {
199
200
  Luciq.setOverAirVersion(overAirVersion.getString("version"),
200
201
  ArgsRegistry.overAirUpdateService.get(overAirVersion.getString("service")));
201
202
  } else {
@@ -204,6 +205,7 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
204
205
  }
205
206
 
206
207
  builder.build();
208
+ LuciqRNLogger.d(NET_TAG, "[init] SDK build complete");
207
209
  }
208
210
  });
209
211
  }
@@ -547,7 +549,6 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
547
549
  }
548
550
 
549
551
 
550
-
551
552
  /**
552
553
  * Removes user attribute if exists.
553
554
  *
@@ -969,6 +970,7 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
969
970
  final String requestHeaders,
970
971
  final String responseHeaders,
971
972
  final double duration) {
973
+ LuciqRNLogger.d(NET_TAG, "[networkLogAndroid-Core] Received from JS: " + method + " " + url + ", status=" + (int) responseCode + ", duration=" + (long) duration + "ms, reqBodyLen=" + (requestBody != null ? requestBody.length() : 0) + ", resBodyLen=" + (responseBody != null ? responseBody.length() : 0));
972
974
  try {
973
975
  final String date = String.valueOf(System.currentTimeMillis());
974
976
 
@@ -985,11 +987,14 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
985
987
  networkLog.setRequestHeaders(requestHeaders);
986
988
  networkLog.setResponseHeaders(responseHeaders);
987
989
  } catch (OutOfMemoryError | Exception exception) {
990
+ LuciqRNLogger.e(NET_TAG, "[networkLogAndroid-Core] OOM/Error setting log contents: " + exception.getMessage() + " for " + method + " " + url);
988
991
  Log.d(TAG, "Error: " + exception.getMessage() + "while trying to set network log contents (request body, response body, request headers, and response headers).");
989
992
  }
990
993
 
991
994
  networkLog.insert();
995
+ LuciqRNLogger.d(NET_TAG, "[networkLogAndroid-Core] Successfully inserted NetworkLog: " + method + " " + url);
992
996
  } catch (OutOfMemoryError | Exception exception) {
997
+ LuciqRNLogger.e(NET_TAG, "[networkLogAndroid-Core] OOM/Error inserting network log: " + exception.getMessage() + " for " + method + " " + url);
993
998
  Log.d(TAG, "Error: " + exception.getMessage() + "while trying to insert a network log");
994
999
  }
995
1000
  }
@@ -998,18 +1003,18 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
998
1003
  @Nullable
999
1004
  private View resolveReactView(final int reactTag) {
1000
1005
  try {
1001
- final ReactApplicationContext reactContext = getReactApplicationContext();
1002
- final UIManagerModule uiManagerModule = reactContext.getNativeModule(UIManagerModule.class);
1006
+ final ReactApplicationContext reactContext = getReactApplicationContext();
1007
+ final UIManagerModule uiManagerModule = reactContext.getNativeModule(UIManagerModule.class);
1003
1008
 
1004
- if (uiManagerModule == null) {
1009
+ if (uiManagerModule == null) {
1005
1010
  UIManager uiNewManagerModule = UIManagerHelper.getUIManagerForReactTag(reactContext, reactTag);
1006
1011
  if (uiNewManagerModule != null) {
1007
1012
  return uiNewManagerModule.resolveView(reactTag);
1008
1013
  }
1009
- return null;
1010
- }
1014
+ return null;
1015
+ }
1011
1016
 
1012
- return uiManagerModule.resolveView(reactTag);
1017
+ return uiManagerModule.resolveView(reactTag);
1013
1018
  } catch (Exception e) {
1014
1019
  return null;
1015
1020
  }
@@ -1024,8 +1029,8 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1024
1029
  try {
1025
1030
  final View view = resolveReactView(reactTag);
1026
1031
 
1027
- if(view !=null){
1028
- Luciq.addPrivateViews(view);
1032
+ if (view != null) {
1033
+ Luciq.addPrivateViews(view);
1029
1034
  }
1030
1035
  } catch (Exception e) {
1031
1036
  e.printStackTrace();
@@ -1041,9 +1046,9 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1041
1046
  public void run() {
1042
1047
  try {
1043
1048
  final View view = resolveReactView(reactTag);
1044
- if(view !=null){
1049
+ if (view != null) {
1045
1050
 
1046
- Luciq.removePrivateViews(view);
1051
+ Luciq.removePrivateViews(view);
1047
1052
  }
1048
1053
  } catch (Exception e) {
1049
1054
  e.printStackTrace();
@@ -1078,7 +1083,7 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1078
1083
  * Reports that the screen has been changed (Repro Steps) the screen sent to this method will be the 'current view' on the dashboard
1079
1084
  *
1080
1085
  * @param screenName string containing the screen name
1081
- * @param spanId the span ID for screen loading tracking (nullable)
1086
+ * @param spanId the span ID for screen loading tracking (nullable)
1082
1087
  */
1083
1088
  @ReactMethod
1084
1089
  public void reportScreenChange(final String screenName, @Nullable final String spanId) {
@@ -1087,9 +1092,9 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1087
1092
  public void run() {
1088
1093
  try {
1089
1094
  Long uiTraceId = spanId != null ? Long.parseLong(spanId) : null;
1090
- Method method = getMethod(Class.forName("ai.luciq.library.Luciq"), "reportScreenChange", Bitmap.class, String.class , Long.class);
1095
+ Method method = getMethod(Class.forName("ai.luciq.library.Luciq"), "reportScreenChange", Bitmap.class, String.class, Long.class);
1091
1096
  if (method != null) {
1092
- method.invoke(null, null, screenName , uiTraceId);
1097
+ method.invoke(null, null, screenName, uiTraceId);
1093
1098
  }
1094
1099
  } catch (Exception e) {
1095
1100
  e.printStackTrace();
@@ -1099,7 +1104,6 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1099
1104
  }
1100
1105
 
1101
1106
 
1102
-
1103
1107
  @ReactMethod
1104
1108
  public void addFeatureFlags(final ReadableMap featureFlagsMap) {
1105
1109
  MainThreadHandler.runOnMainThread(new Runnable() {
@@ -1172,7 +1176,7 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1172
1176
  */
1173
1177
  @ReactMethod
1174
1178
  public void registerFeatureFlagsChangeListener() {
1175
-
1179
+ LuciqRNLogger.d(NET_TAG, "[registerFeatureFlagsChangeListener] Registering native feature flags listener");
1176
1180
  MainThreadHandler.runOnMainThread(new Runnable() {
1177
1181
  @Override
1178
1182
  public void run() {
@@ -1180,16 +1184,19 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1180
1184
  InternalCore.INSTANCE._setFeaturesStateListener(new FeaturesStateListener() {
1181
1185
  @Override
1182
1186
  public void invoke(@NonNull CoreFeaturesState featuresState) {
1187
+ LuciqRNLogger.d(NET_TAG, "[FeatureFlagsListener] Received update — W3CTraceID=" + featuresState.isW3CExternalTraceIdEnabled() + ", generatedHeader=" + featuresState.isAttachingGeneratedHeaderEnabled() + ", caughtHeader=" + featuresState.isAttachingCapturedHeaderEnabled() + ", networkBodyLimit=" + featuresState.getNetworkLogCharLimit());
1183
1188
  WritableMap params = Arguments.createMap();
1184
1189
  params.putBoolean("isW3ExternalTraceIDEnabled", featuresState.isW3CExternalTraceIdEnabled());
1185
1190
  params.putBoolean("isW3ExternalGeneratedHeaderEnabled", featuresState.isAttachingGeneratedHeaderEnabled());
1186
1191
  params.putBoolean("isW3CaughtHeaderEnabled", featuresState.isAttachingCapturedHeaderEnabled());
1187
- params.putInt("networkBodyLimit",featuresState.getNetworkLogCharLimit());
1192
+ params.putInt("networkBodyLimit", featuresState.getNetworkLogCharLimit());
1188
1193
 
1189
1194
  sendEvent(Constants.LCQ_ON_FEATURE_FLAGS_UPDATE_RECEIVED_CALLBACK, params);
1195
+ LuciqRNLogger.d(NET_TAG, "[FeatureFlagsListener] Sent event to JS: " + Constants.LCQ_ON_FEATURE_FLAGS_UPDATE_RECEIVED_CALLBACK);
1190
1196
  }
1191
1197
  });
1192
1198
  } catch (Exception e) {
1199
+ LuciqRNLogger.e(NET_TAG, "[registerFeatureFlagsChangeListener] Failed to register listener", e);
1193
1200
  e.printStackTrace();
1194
1201
  }
1195
1202
 
@@ -1204,13 +1211,16 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1204
1211
  */
1205
1212
  @ReactMethod
1206
1213
  public void isW3ExternalTraceIDEnabled(Promise promise) {
1207
-
1214
+ LuciqRNLogger.d(NET_TAG, "[isW3ExternalTraceIDEnabled] Querying native flag");
1208
1215
  MainThreadHandler.runOnMainThread(new Runnable() {
1209
1216
  @Override
1210
1217
  public void run() {
1211
1218
  try {
1212
- promise.resolve(InternalCore.INSTANCE._isFeatureEnabled(CoreFeature.W3C_EXTERNAL_TRACE_ID));
1219
+ boolean enabled = InternalCore.INSTANCE._isFeatureEnabled(CoreFeature.W3C_EXTERNAL_TRACE_ID);
1220
+ LuciqRNLogger.d(NET_TAG, "[isW3ExternalTraceIDEnabled] Result=" + enabled);
1221
+ promise.resolve(enabled);
1213
1222
  } catch (Exception e) {
1223
+ LuciqRNLogger.e(NET_TAG, "[isW3ExternalTraceIDEnabled] Error querying flag", e);
1214
1224
  e.printStackTrace();
1215
1225
  promise.resolve(false);
1216
1226
  }
@@ -1226,13 +1236,16 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1226
1236
  */
1227
1237
  @ReactMethod
1228
1238
  public void isW3ExternalGeneratedHeaderEnabled(Promise promise) {
1229
-
1239
+ LuciqRNLogger.d(NET_TAG, "[isW3ExternalGeneratedHeaderEnabled] Querying native flag");
1230
1240
  MainThreadHandler.runOnMainThread(new Runnable() {
1231
1241
  @Override
1232
1242
  public void run() {
1233
1243
  try {
1234
- promise.resolve(InternalCore.INSTANCE._isFeatureEnabled(CoreFeature.W3C_ATTACHING_GENERATED_HEADER));
1244
+ boolean enabled = InternalCore.INSTANCE._isFeatureEnabled(CoreFeature.W3C_ATTACHING_GENERATED_HEADER);
1245
+ LuciqRNLogger.d(NET_TAG, "[isW3ExternalGeneratedHeaderEnabled] Result=" + enabled);
1246
+ promise.resolve(enabled);
1235
1247
  } catch (Exception e) {
1248
+ LuciqRNLogger.e(NET_TAG, "[isW3ExternalGeneratedHeaderEnabled] Error querying flag", e);
1236
1249
  e.printStackTrace();
1237
1250
  promise.resolve(false);
1238
1251
  }
@@ -1247,13 +1260,16 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1247
1260
  */
1248
1261
  @ReactMethod
1249
1262
  public void isW3CaughtHeaderEnabled(Promise promise) {
1250
-
1263
+ LuciqRNLogger.d(NET_TAG, "[isW3CaughtHeaderEnabled] Querying native flag");
1251
1264
  MainThreadHandler.runOnMainThread(new Runnable() {
1252
1265
  @Override
1253
1266
  public void run() {
1254
1267
  try {
1255
- promise.resolve(InternalCore.INSTANCE._isFeatureEnabled(CoreFeature.W3C_ATTACHING_CAPTURED_HEADER));
1268
+ boolean enabled = InternalCore.INSTANCE._isFeatureEnabled(CoreFeature.W3C_ATTACHING_CAPTURED_HEADER);
1269
+ LuciqRNLogger.d(NET_TAG, "[isW3CaughtHeaderEnabled] Result=" + enabled);
1270
+ promise.resolve(enabled);
1256
1271
  } catch (Exception e) {
1272
+ LuciqRNLogger.e(NET_TAG, "[isW3CaughtHeaderEnabled] Error querying flag", e);
1257
1273
  e.printStackTrace();
1258
1274
  promise.resolve(false);
1259
1275
  }
@@ -1268,7 +1284,7 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1268
1284
  * Map between the exported JS constant and the arg key in {@link ArgsRegistry}.
1269
1285
  * The constant name and the arg key should match to be able to resolve the
1270
1286
  * constant with its actual value from the {@link ArgsRegistry} maps.
1271
- *
1287
+ * <p>
1272
1288
  * This is a workaround, because RN cannot resolve enums in the constants map.
1273
1289
  */
1274
1290
  @Override
@@ -1299,23 +1315,25 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1299
1315
  }
1300
1316
  });
1301
1317
  }
1318
+
1302
1319
  /**
1303
- * Enables or disables capturing network body.
1304
- * @param isEnabled A boolean to enable/disable capturing network body.
1305
- */
1306
- @ReactMethod
1307
- public void setNetworkLogBodyEnabled(final boolean isEnabled) {
1308
- MainThreadHandler.runOnMainThread(new Runnable() {
1309
- @Override
1310
- public void run() {
1311
- try {
1312
- Luciq.setNetworkLogBodyEnabled(isEnabled);
1313
- } catch (Exception e) {
1314
- e.printStackTrace();
1315
- }
1316
- }
1317
- });
1318
- }
1320
+ * Enables or disables capturing network body.
1321
+ *
1322
+ * @param isEnabled A boolean to enable/disable capturing network body.
1323
+ */
1324
+ @ReactMethod
1325
+ public void setNetworkLogBodyEnabled(final boolean isEnabled) {
1326
+ MainThreadHandler.runOnMainThread(new Runnable() {
1327
+ @Override
1328
+ public void run() {
1329
+ try {
1330
+ Luciq.setNetworkLogBodyEnabled(isEnabled);
1331
+ } catch (Exception e) {
1332
+ e.printStackTrace();
1333
+ }
1334
+ }
1335
+ });
1336
+ }
1319
1337
 
1320
1338
  /**
1321
1339
  * Sets the auto mask screenshots types.
@@ -1347,13 +1365,16 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1347
1365
  */
1348
1366
  @ReactMethod
1349
1367
  public void getNetworkBodyMaxSize(Promise promise) {
1350
-
1368
+ LuciqRNLogger.d(NET_TAG, "[getNetworkBodyMaxSize] Querying network body size limit");
1351
1369
  MainThreadHandler.runOnMainThread(new Runnable() {
1352
1370
  @Override
1353
1371
  public void run() {
1354
1372
  try {
1355
- promise.resolve(InternalCore.INSTANCE.get_networkLogCharLimit());
1373
+ Object limit = InternalCore.INSTANCE.get_networkLogCharLimit();
1374
+ LuciqRNLogger.d(NET_TAG, "[getNetworkBodyMaxSize] Result=" + limit);
1375
+ promise.resolve(limit);
1356
1376
  } catch (Exception e) {
1377
+ LuciqRNLogger.e(NET_TAG, "[getNetworkBodyMaxSize] Error querying limit", e);
1357
1378
  e.printStackTrace();
1358
1379
  promise.resolve(false);
1359
1380
  }
@@ -1361,20 +1382,20 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1361
1382
  });
1362
1383
  }
1363
1384
 
1364
- /**
1365
- * Sets current App variant
1366
- *
1367
- * @param appVariant The app variant name .
1368
- */
1369
- @ReactMethod
1370
- public void setAppVariant(@NonNull String appVariant) {
1371
- try {
1372
- Luciq.setAppVariant(appVariant);
1385
+ /**
1386
+ * Sets current App variant
1387
+ *
1388
+ * @param appVariant The app variant name .
1389
+ */
1390
+ @ReactMethod
1391
+ public void setAppVariant(@NonNull String appVariant) {
1392
+ try {
1393
+ Luciq.setAppVariant(appVariant);
1373
1394
 
1374
- } catch (Exception e) {
1375
- e.printStackTrace();
1376
- }
1395
+ } catch (Exception e) {
1396
+ e.printStackTrace();
1377
1397
  }
1398
+ }
1378
1399
 
1379
1400
  /**
1380
1401
  * Enables or disables WebView monitoring.
@@ -1505,17 +1526,16 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1505
1526
  }
1506
1527
 
1507
1528
 
1508
-
1509
1529
  /**
1510
1530
  * Applies a color to the theme builder if present in the configuration.
1511
1531
  *
1512
1532
  * @param themeConfig The theme configuration map
1513
- * @param builder The theme builder
1514
- * @param key The configuration key
1515
- * @param setter The color setter function
1533
+ * @param builder The theme builder
1534
+ * @param key The configuration key
1535
+ * @param setter The color setter function
1516
1536
  */
1517
1537
  private void applyColorIfPresent(ReadableMap themeConfig, ai.luciq.library.model.LuciqTheme.Builder builder,
1518
- String key, java.util.function.BiConsumer<ai.luciq.library.model.LuciqTheme.Builder, Integer> setter) {
1538
+ String key, java.util.function.BiConsumer<ai.luciq.library.model.LuciqTheme.Builder, Integer> setter) {
1519
1539
  if (themeConfig.hasKey(key)) {
1520
1540
  int color = getColor(themeConfig, key);
1521
1541
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -1528,12 +1548,12 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1528
1548
  * Applies a text style to the theme builder if present in the configuration.
1529
1549
  *
1530
1550
  * @param themeConfig The theme configuration map
1531
- * @param builder The theme builder
1532
- * @param key The configuration key
1533
- * @param setter The text style setter function
1551
+ * @param builder The theme builder
1552
+ * @param key The configuration key
1553
+ * @param setter The text style setter function
1534
1554
  */
1535
1555
  private void applyTextStyleIfPresent(ReadableMap themeConfig, ai.luciq.library.model.LuciqTheme.Builder builder,
1536
- String key, java.util.function.BiConsumer<ai.luciq.library.model.LuciqTheme.Builder, Integer> setter) {
1556
+ String key, java.util.function.BiConsumer<ai.luciq.library.model.LuciqTheme.Builder, Integer> setter) {
1537
1557
  if (themeConfig.hasKey(key)) {
1538
1558
  int style = getTextStyle(themeConfig, key);
1539
1559
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -1546,13 +1566,13 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1546
1566
  * Sets a font on the theme builder if the font configuration is present in the theme config.
1547
1567
  *
1548
1568
  * @param themeConfig The theme configuration map
1549
- * @param builder The theme builder
1550
- * @param fileKey The key for font file path
1551
- * @param assetKey The key for font asset path
1552
- * @param fontType The type of font (for logging purposes)
1569
+ * @param builder The theme builder
1570
+ * @param fileKey The key for font file path
1571
+ * @param assetKey The key for font asset path
1572
+ * @param fontType The type of font (for logging purposes)
1553
1573
  */
1554
1574
  private void setFontIfPresent(ReadableMap themeConfig, ai.luciq.library.model.LuciqTheme.Builder builder,
1555
- String fileKey, String assetKey, String fontType) {
1575
+ String fileKey, String assetKey, String fontType) {
1556
1576
  if (themeConfig.hasKey(fileKey) || themeConfig.hasKey(assetKey)) {
1557
1577
  Typeface typeface = getTypeface(themeConfig, fileKey, assetKey);
1558
1578
  if (typeface != null) {
@@ -1637,27 +1657,28 @@ public class RNLuciqReactnativeModule extends EventEmitterModule {
1637
1657
  return Typeface.DEFAULT;
1638
1658
  }
1639
1659
 
1640
- /**
1641
- * Extracts the filename from a path, removing any directory prefixes.
1642
- *
1643
- * @param path The full path to the file
1644
- * @return Just the filename with extension
1645
- */
1646
- private String getFileName(String path) {
1647
- if (path == null || path.isEmpty()) {
1648
- return path;
1649
- }
1660
+ /**
1661
+ * Extracts the filename from a path, removing any directory prefixes.
1662
+ *
1663
+ * @param path The full path to the file
1664
+ * @return Just the filename with extension
1665
+ */
1666
+ private String getFileName(String path) {
1667
+ if (path == null || path.isEmpty()) {
1668
+ return path;
1669
+ }
1650
1670
 
1651
- int lastSeparator = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
1652
- if (lastSeparator >= 0 && lastSeparator < path.length() - 1) {
1653
- return path.substring(lastSeparator + 1);
1654
- }
1671
+ int lastSeparator = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
1672
+ if (lastSeparator >= 0 && lastSeparator < path.length() - 1) {
1673
+ return path.substring(lastSeparator + 1);
1674
+ }
1655
1675
 
1656
- return path;
1657
- }
1676
+ return path;
1677
+ }
1658
1678
 
1659
- /**
1679
+ /**
1660
1680
  * Enables or disables displaying in full-screen mode, hiding the status and navigation bars.
1681
+ *
1661
1682
  * @param isEnabled A boolean to enable/disable setFullscreen.
1662
1683
  */
1663
1684
  @ReactMethod
@@ -1666,7 +1687,7 @@ private String getFileName(String path) {
1666
1687
  @Override
1667
1688
  public void run() {
1668
1689
  try {
1669
- Luciq.setFullscreen(isEnabled);
1690
+ Luciq.setFullscreen(isEnabled);
1670
1691
  } catch (Exception e) {
1671
1692
  e.printStackTrace();
1672
1693
  }
@@ -10,6 +10,7 @@ import com.facebook.react.bridge.WritableMap;
10
10
  import com.facebook.react.modules.core.DeviceEventManagerModule;
11
11
 
12
12
  public abstract class EventEmitterModule extends ReactContextBaseJavaModule {
13
+ private static final String NET_TAG = "LCQ-RN-NET";
13
14
  private int listenerCount = 0;
14
15
 
15
16
  public EventEmitterModule(ReactApplicationContext context) {
@@ -22,14 +23,18 @@ public abstract class EventEmitterModule extends ReactContextBaseJavaModule {
22
23
  getReactApplicationContext()
23
24
  .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
24
25
  .emit(event, params);
26
+ } else {
27
+ LuciqRNLogger.w(NET_TAG, "[EventEmitter] Event DROPPED (no JS listeners): event=" + event + ", module=" + getName() + ", listenerCount=0");
25
28
  }
26
29
  }
27
30
 
28
31
  protected void addListener(String ignoredEvent) {
29
32
  listenerCount++;
33
+ LuciqRNLogger.d(NET_TAG, "[EventEmitter] addListener — module=" + getName() + ", event=" + ignoredEvent + ", listenerCount=" + listenerCount);
30
34
  }
31
35
 
32
36
  protected void removeListeners(Integer count) {
33
37
  listenerCount -= count;
38
+ LuciqRNLogger.d(NET_TAG, "[EventEmitter] removeListeners — module=" + getName() + ", removed=" + count + ", listenerCount=" + listenerCount);
34
39
  }
35
40
  }
@@ -0,0 +1,53 @@
1
+ package ai.luciq.reactlibrary.utils;
2
+
3
+ import android.util.Log;
4
+
5
+ import ai.luciq.library.LogLevel;
6
+
7
+ /**
8
+ * Bridge-side logger that gates android.util.Log calls on the same
9
+ * debugLogsLevel the host app passes to Luciq.init(), so the native
10
+ * RN bridge diagnostic logs do not leak in production builds when the
11
+ * JS-side Logger is silent.
12
+ *
13
+ * Mirrors the level hierarchy in src/utils/logger.ts:
14
+ * VERBOSE > DEBUG > ERROR > NONE
15
+ */
16
+ public final class LuciqRNLogger {
17
+
18
+ private static volatile int currentLevel = LogLevel.ERROR;
19
+
20
+ private LuciqRNLogger() {}
21
+
22
+ public static void setLevel(int level) {
23
+ currentLevel = level;
24
+ }
25
+
26
+ public static int getLevel() {
27
+ return currentLevel;
28
+ }
29
+
30
+ public static void d(String tag, String message) {
31
+ if (currentLevel >= LogLevel.DEBUG) {
32
+ Log.d(tag, message);
33
+ }
34
+ }
35
+
36
+ public static void w(String tag, String message) {
37
+ if (currentLevel >= LogLevel.DEBUG) {
38
+ Log.w(tag, message);
39
+ }
40
+ }
41
+
42
+ public static void e(String tag, String message) {
43
+ if (currentLevel >= LogLevel.ERROR) {
44
+ Log.e(tag, message);
45
+ }
46
+ }
47
+
48
+ public static void e(String tag, String message, Throwable throwable) {
49
+ if (currentLevel >= LogLevel.ERROR) {
50
+ Log.e(tag, message, throwable);
51
+ }
52
+ }
53
+ }
@@ -1,7 +1,7 @@
1
1
  import { AppState, findNodeHandle, Platform } from 'react-native';
2
2
  import Report from '../models/Report';
3
3
  import { emitter, NativeEvents, NativeLuciq } from '../native/NativeLuciq';
4
- import { registerFeatureFlagsListener } from '../utils/FeatureFlags';
4
+ import { registerFeatureFlagsListener, initFeatureFlagsCache } from '../utils/FeatureFlags';
5
5
  import { LogLevel, NetworkInterceptionMode, ReproStepsMode, StringKey, } from '../utils/Enums';
6
6
  import LuciqUtils, { checkNetworkRequestHandlers, resetNativeObfuscationListener, setApmNetworkFlagsIfChanged, stringifyIfNotString, } from '../utils/LuciqUtils';
7
7
  import * as NetworkLogger from './NetworkLogger';
@@ -57,10 +57,19 @@ function reportCurrentViewForAndroid(screenName) {
57
57
  * @param config SDK configurations. See {@link LuciqConfig} for more info.
58
58
  */
59
59
  export const init = (config) => {
60
+ initFeatureFlagsCache();
60
61
  if (Platform.OS === 'android') {
61
62
  // Add android feature flags listener for android
62
63
  registerFeatureFlagsListener();
63
64
  addOnFeatureUpdatedListener(config);
65
+ // Enable the JS XHR interceptor synchronously so cold-start requests
66
+ // (fired before LCQ_ON_FEATURES_UPDATED_CALLBACK arrives) are captured.
67
+ handleNetworkInterceptionMode(config);
68
+ setApmNetworkFlagsIfChanged({
69
+ isNativeInterceptionFeatureEnabled: isNativeInterceptionFeatureEnabled,
70
+ hasAPMNetworkPlugin: hasAPMNetworkPlugin,
71
+ shouldEnableNativeInterception: shouldEnableNativeInterception,
72
+ });
64
73
  }
65
74
  else {
66
75
  isNativeInterceptionFeatureEnabled = NativeNetworkLogger.isNativeInterceptionEnabled();