react-native-shield-fraud-plugin 1.0.10 → 2.0.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.
Files changed (27) hide show
  1. package/android/build.gradle +30 -12
  2. package/android/src/main/java/com/shieldfraudplugin/ShieldFraudPluginModule.java +354 -123
  3. package/android/src/main/java/com/shieldfraudplugin/ShieldFraudPluginPackage.java +71 -21
  4. package/android/src/newarch/java/com/shieldfraudplugin/ShieldFraudPluginSpec.java +41 -0
  5. package/android/src/oldarch/java/com/shieldfraudplugin/ShieldFraudPluginSpec.java +36 -0
  6. package/ios/ShieldFraudPlugin.h +11 -2
  7. package/ios/ShieldFraudPlugin.mm +289 -0
  8. package/lib/commonjs/NativeShieldFraudPlugin.js +15 -0
  9. package/lib/commonjs/NativeShieldFraudPlugin.js.map +1 -0
  10. package/lib/commonjs/index.js +80 -8
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/module/NativeShieldFraudPlugin.js +10 -0
  13. package/lib/module/NativeShieldFraudPlugin.js.map +1 -0
  14. package/lib/module/index.js +81 -9
  15. package/lib/module/index.js.map +1 -1
  16. package/lib/typescript/NativeShieldFraudPlugin.d.ts +52 -0
  17. package/lib/typescript/NativeShieldFraudPlugin.d.ts.map +1 -0
  18. package/lib/typescript/index.d.ts +41 -3
  19. package/lib/typescript/index.d.ts.map +1 -1
  20. package/package.json +10 -2
  21. package/react-native-shield-fraud-plugin.podspec +13 -3
  22. package/src/NativeShieldFraudPlugin.ts +85 -0
  23. package/src/index.tsx +115 -11
  24. package/ios/ShieldFraudPlugin.m +0 -98
  25. package/ios/ShieldFraudPlugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  26. package/ios/ShieldFraudPlugin.xcodeproj/project.xcworkspace/xcuserdata/deepakraj.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  27. package/ios/ShieldFraudPlugin.xcodeproj/xcuserdata/deepakraj.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
@@ -15,9 +15,6 @@ def isNewArchitectureEnabled() {
15
15
 
16
16
  apply plugin: "com.android.library"
17
17
 
18
-
19
- def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
20
-
21
18
  if (isNewArchitectureEnabled()) {
22
19
  apply plugin: "com.facebook.react"
23
20
  }
@@ -33,11 +30,17 @@ def getExtOrIntegerDefault(name) {
33
30
  android {
34
31
  compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
35
32
 
33
+ // Conditional for compatibility with AGP <4.2.
34
+ if (project.android.hasProperty("namespace")) {
35
+ namespace 'com.shieldfraudplugin'
36
+ }
37
+
36
38
  defaultConfig {
37
39
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
38
40
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
39
41
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
40
42
  }
43
+
41
44
  buildTypes {
42
45
  release {
43
46
  minifyEnabled false
@@ -53,6 +56,18 @@ android {
53
56
  targetCompatibility JavaVersion.VERSION_1_8
54
57
  }
55
58
 
59
+ sourceSets {
60
+ main {
61
+ // Select the architecture-specific ShieldFraudPluginSpec shim at build time.
62
+ // - oldarch: extends ReactContextBaseJavaModule (Old Architecture / bridge)
63
+ // - newarch: extends the codegen-generated NativeShieldFraudPluginSpec (New Architecture)
64
+ if (isNewArchitectureEnabled()) {
65
+ java.srcDirs += ['src/newarch']
66
+ } else {
67
+ java.srcDirs += ['src/oldarch']
68
+ }
69
+ }
70
+ }
56
71
 
57
72
  if (isNewArchitectureEnabled()) {
58
73
  react {
@@ -65,21 +80,24 @@ android {
65
80
 
66
81
  rootProject.allprojects {
67
82
  repositories {
68
- mavenCentral()
69
- google()
70
- maven {
71
- url "https://cashshield-sdk.s3.amazonaws.com/release/"
72
- }
83
+ mavenCentral()
84
+ google()
85
+ maven {
86
+ url "https://cashshield-sdk.s3.amazonaws.com/release/"
87
+ content {
88
+ includeGroup "com.shield.android"
89
+ includeGroup "com.shield.android.module"
90
+ }
91
+ }
73
92
  }
74
93
  }
75
94
 
76
-
77
-
78
95
  dependencies {
79
96
  // For < 0.71, this will be from the local maven repo
80
97
  // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
81
98
  //noinspection GradleDynamicVersion
82
99
  implementation "com.facebook.react:react-native:+"
83
- implementation 'com.shield.android:shield-fraud:1.6.+'
84
- }
85
100
 
101
+ // Shield Fraud SDK 2.x — factory-based init, callback-based device intelligence
102
+ implementation 'com.shield.android:shield-fraud:2.4.0'
103
+ }
@@ -1,40 +1,79 @@
1
1
  package com.shieldfraudplugin;
2
2
 
3
- import android.app.Activity;
4
- import android.os.Handler;
5
- import android.os.Looper;
3
+ import android.app.Application;
6
4
 
7
5
  import androidx.annotation.NonNull;
6
+ import androidx.annotation.Nullable;
8
7
 
9
8
  import com.facebook.react.bridge.Arguments;
9
+ import com.facebook.react.bridge.Callback;
10
10
  import com.facebook.react.bridge.Promise;
11
11
  import com.facebook.react.bridge.ReactApplicationContext;
12
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
13
12
  import com.facebook.react.bridge.ReactMethod;
13
+ import com.facebook.react.bridge.ReadableMap;
14
+ import com.facebook.react.bridge.WritableArray;
14
15
  import com.facebook.react.bridge.WritableMap;
15
16
  import com.facebook.react.module.annotations.ReactModule;
16
-
17
- import com.facebook.react.bridge.Callback;
18
- import com.facebook.react.bridge.ReadableMap;
19
17
  import com.facebook.react.modules.core.DeviceEventManagerModule;
18
+
20
19
  import com.shield.android.BlockedDialog;
20
+ import com.shield.android.DeviceIntelligence;
21
+ import com.shield.android.Environment;
22
+ import com.shield.android.LogLevel;
23
+ import com.shield.android.Result;
21
24
  import com.shield.android.Shield;
22
- import com.shield.android.ShieldCallback;
23
- import com.shield.android.ShieldException;
25
+ import com.shield.android.ShieldConfig;
26
+ import com.shield.android.ShieldCrossPlatformHelper;
27
+ import com.shield.android.ShieldCrossPlatformParams;
28
+ import com.shield.android.ShieldError;
29
+ import com.shield.android.ShieldFactory;
24
30
 
31
+ import org.json.JSONArray;
25
32
  import org.json.JSONObject;
26
33
 
27
34
  import java.util.HashMap;
35
+ import java.util.Iterator;
28
36
  import java.util.Map;
29
37
 
30
- import javax.annotation.Nonnull;
31
- import javax.annotation.Nullable;
32
-
38
+ /**
39
+ * ShieldFraudPlugin native module — Shield SDK 2.x (pure Java)
40
+ *
41
+ * SDK 2.x migration summary
42
+ * ─────────────────────────
43
+ * 1. Initialisation : Shield.Builder → ShieldConfig + ShieldFactory
44
+ * 2. Instance access : Shield.getInstance() → stored `shield` member
45
+ * 3. Session ID : Shield.getInstance().getSessionId() → shield.getSessionId()
46
+ * 4. Device results : setDeviceResultStateListener(callback)
47
+ * → ShieldFactory.createShieldWithCallback (SHIELD Sentinel)
48
+ * 5. sendAttributes : void return
49
+ * → shield.sendAttributesWithCallback (Result<String> callback)
50
+ * 6. LogLevel enum : Shield.LogLevel.VERBOSE/DEBUG/INFO/NONE
51
+ * → LogLevel.DEBUG / INFO / NONE (VERBOSE removed)
52
+ * 7. Environment enum : String constants ENVIRONMENT_PROD / DEV / STAGING
53
+ * → Environment.PROD / DEV (STAGING removed)
54
+ *
55
+ * Architecture segmentation
56
+ * ─────────────────────────
57
+ * Handled by Gradle source sets (not #ifdef).
58
+ * src/oldarch/ShieldFraudPluginSpec.java → extends ReactContextBaseJavaModule
59
+ * src/newarch/ShieldFraudPluginSpec.java → extends NativeShieldFraudPluginSpec (codegen)
60
+ * This module always extends ShieldFraudPluginSpec.
61
+ */
33
62
  @ReactModule(name = ShieldFraudPluginModule.NAME)
34
- public class ShieldFraudPluginModule extends ReactContextBaseJavaModule implements ShieldCallback<JSONObject> {
63
+ public class ShieldFraudPluginModule extends com.shieldfraudplugin.ShieldFraudPluginSpec {
64
+
35
65
  public static final String NAME = "ShieldFraudPlugin";
66
+
36
67
  private final ReactApplicationContext reactContext;
37
68
 
69
+ /**
70
+ * Stored Shield instance — created once by ShieldFactory in initShield().
71
+ * All subsequent calls use this reference instead of a singleton accessor.
72
+ */
73
+ @Nullable
74
+ private Shield shield = null;
75
+
76
+
38
77
  public ShieldFraudPluginModule(ReactApplicationContext reactContext) {
39
78
  super(reactContext);
40
79
  this.reactContext = reactContext;
@@ -46,158 +85,350 @@ public class ShieldFraudPluginModule extends ReactContextBaseJavaModule implemen
46
85
  return NAME;
47
86
  }
48
87
 
88
+ // =========================================================================
89
+ // Synchronous state queries
90
+ // =========================================================================
91
+
49
92
  @ReactMethod(isBlockingSynchronousMethod = true)
50
93
  public boolean isShieldInitialized() {
51
- try {
52
- return Shield.getInstance() != null;
53
- } catch (IllegalStateException e) {
54
- return false;
55
- }
94
+ return shield != null;
95
+ }
96
+
97
+ @ReactMethod(isBlockingSynchronousMethod = true)
98
+ public String getSessionId() {
99
+ return shield != null ? shield.getSessionId() : "";
56
100
  }
57
101
 
102
+ // =========================================================================
103
+ // SDK initialisation (Shield SDK 2.x — ShieldConfig + ShieldFactory)
104
+ //
105
+ // createShieldWithCallback both initialises the SDK and acts as the
106
+ // SHIELD Sentinel callback — it fires on the initial fingerprint and on
107
+ // every subsequent device intelligence update during the session.
108
+ //
109
+ // logLevel / environmentInfo arrive as `double` from both architectures:
110
+ // • Old Arch — JS bridge maps JS numbers to double.
111
+ // • New Arch — codegen generates double for TypeScript number params.
112
+ // =========================================================================
113
+
58
114
  @ReactMethod
59
- public void initShield(String siteID, String secretKey, Boolean isOptimizedListener, ReadableMap blockedDialog, Integer logLevel, Integer environment) {
115
+ public void initShield(
116
+ String siteID,
117
+ String secretKey,
118
+ boolean isOptimizedListener,
119
+ @Nullable ReadableMap blockedDialog,
120
+ double logLevel,
121
+ double environmentInfo,
122
+ boolean blockScreenRecording,
123
+ Promise promise) {
124
+
60
125
  if (isShieldInitialized()) {
126
+ promise.resolve(null);
61
127
  return;
62
128
  }
63
- Activity currentActivity = getCurrentActivity();
64
- if (currentActivity != null) {
65
- Shield.Builder builder = new Shield.Builder(currentActivity, siteID, secretKey)
66
- .setAutoBlockDialog(getBlockedDialogFromReadableMap(blockedDialog));
67
129
 
68
- if (isOptimizedListener) {
69
- builder.registerDeviceShieldCallback(this);
70
- }
130
+ Application application = (Application) reactContext.getApplicationContext();
131
+
132
+ ShieldConfig config = new ShieldConfig(siteID, secretKey);
133
+ config.setLogLevel(getLogLevelFromInt((int) logLevel));
134
+ config.setEnvironment(getEnvironmentFromInt((int) environmentInfo));
71
135
 
72
- String environmentString = getEnvironmentStringFromInteger(environment);
73
- builder.setEnvironment(environmentString);
136
+ if (blockedDialog != null) {
137
+ String title = blockedDialog.hasKey("title") ? blockedDialog.getString("title") : null;
138
+ String body = blockedDialog.hasKey("body") ? blockedDialog.getString("body") : null;
139
+ config.setBlockedDialog(new BlockedDialog(title, body));
140
+ }
74
141
 
75
- Shield.LogLevel logLevelEnum = getLogLevelEnumFromInteger(logLevel);
76
- builder.setLogLevel(logLevelEnum);
142
+ // blockScreenRecording is Android-only. JS always sends a real boolean
143
+ // (true/false, never null) so the Old Arch bridge can safely read it as
144
+ // a primitive. Defaults to false when the user doesn't set it in Config.
145
+ config.setBlockScreenRecording(blockScreenRecording);
77
146
 
78
- Shield shield = builder.build();
79
- Shield.setSingletonInstance(shield);
147
+ if (isOptimizedListener) {
148
+ // SDK 2.x: createShieldWithCallback initialises + acts as the Sentinel.
149
+ // Unlike 1.x (which required a separate setDeviceResultStateListener() call
150
+ // on the SDK), 2.x fires this callback automatically when device intelligence
151
+ // is ready and on every subsequent risk-profile change.
152
+ // The callback may arrive on a background thread — always post to main looper.
153
+ shield = ShieldFactory.createShieldWithCallback(
154
+ application,
155
+ config,
156
+ result -> new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
157
+ if (result instanceof Result.Success) {
158
+ DeviceIntelligence di =
159
+ ((Result.Success<DeviceIntelligence>) result).getData();
160
+ JSONObject json = di != null ? di.getData() : null;
161
+ emitEvent("success", json != null ? json.toString() : null);
162
+ } else if (result instanceof Result.Failure) {
163
+ ShieldError error =
164
+ ((Result.Failure<DeviceIntelligence>) result).getError();
165
+ emitEvent("error", error.getErrorMessage());
166
+ }
167
+ }));
168
+ } else {
169
+ // createShield = initialise without the real-time Sentinel callback.
170
+ // Use getLatestDeviceResult() to poll on demand instead.
171
+ shield = ShieldFactory.createShield(application, config);
80
172
  }
173
+
174
+ promise.resolve(null);
81
175
  }
82
176
 
83
- private BlockedDialog getBlockedDialogFromReadableMap(ReadableMap blockedDialog) {
84
- if (blockedDialog != null) {
85
- String title = blockedDialog.hasKey("title") ? blockedDialog.getString("title") : null;
86
- String body = blockedDialog.hasKey("body") ? blockedDialog.getString("body") : null;
87
- return new BlockedDialog(title, body);
88
- }
89
- return null;
177
+ // =========================================================================
178
+ // Cross-platform metadata
179
+ // =========================================================================
180
+
181
+ @ReactMethod
182
+ public void setCrossPlatformParameters(String crossPlatformName, String crossPlatformVersion) {
183
+ ShieldCrossPlatformParams params =
184
+ new ShieldCrossPlatformParams(crossPlatformName, crossPlatformVersion);
185
+ ShieldCrossPlatformHelper.setCrossPlatformParameters(params);
90
186
  }
91
187
 
92
- private String getEnvironmentStringFromInteger(Integer environment) {
93
- String environmentString;
94
- switch (environment) {
95
- case 1:
96
- environmentString = Shield.ENVIRONMENT_DEV;
97
- break;
98
- case 2:
99
- environmentString = Shield.ENVIRONMENT_STAGING;
100
- break;
101
- default:
102
- environmentString = Shield.ENVIRONMENT_PROD;
103
- break;
104
- }
105
- return environmentString;
188
+ // =========================================================================
189
+ // setDeviceResultStateListener — no-op stub (SDK 2.x)
190
+ //
191
+ // SDK 1.x required a manual subscription call + Handler.postDelayed workaround.
192
+ // SDK 2.x fires device result events automatically via createShieldWithCallback
193
+ // when isOptimizedListener == true — no subscription call needed.
194
+ //
195
+ // The method is kept as a no-op stub so the shared TypeScript codegen spec
196
+ // and iOS (which still calls the SDK method) remain unaffected.
197
+ // =========================================================================
198
+
199
+ @ReactMethod
200
+ public void setDeviceResultStateListener() {
201
+ // No-op on Android SDK 2.x — device result events are delivered
202
+ // automatically through the createShieldWithCallback Sentinel.
106
203
  }
107
204
 
108
- private Shield.LogLevel getLogLevelEnumFromInteger(Integer logLevel) {
109
- Shield.LogLevel logLevelEnum;
110
- switch (logLevel) {
111
- case 1:
112
- logLevelEnum = Shield.LogLevel.NONE;
113
- break;
114
- case 2:
115
- logLevelEnum = Shield.LogLevel.INFO;
116
- break;
117
- case 3:
118
- logLevelEnum = Shield.LogLevel.DEBUG;
119
- break;
120
- default:
121
- logLevelEnum = Shield.LogLevel.VERBOSE;
122
- break;
205
+ // =========================================================================
206
+ // Retrieve the latest device intelligence result
207
+ //
208
+ // Returns Shield SDK's latest device result payload.
209
+ // =========================================================================
210
+
211
+ @ReactMethod
212
+ public void getLatestDeviceResult(Callback successCallback, Callback errorCallback) {
213
+ if (shield == null) {
214
+ errorCallback.invoke("Shield SDK is not initialized.");
215
+ return;
216
+ }
217
+
218
+ JSONObject json = shield.getLatestDeviceResult();
219
+ if (json == null) {
220
+ errorCallback.invoke("No device result available yet.");
221
+ return;
123
222
  }
124
- return logLevelEnum;
223
+
224
+ successCallback.invoke(toWritableMap(json));
125
225
  }
126
226
 
227
+ // =========================================================================
228
+ // Send user / screen attributes (Shield SDK 2.x — sendAttributesWithCallback)
229
+ //
230
+ // sendAttributesWithCallback delivers a Result<String> where the String
231
+ // is the sessionId on success.
232
+ // =========================================================================
233
+
127
234
  @ReactMethod
128
- public void setDeviceResultStateListener() {
129
- new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
130
- @Override
131
- public void run() {
132
- Shield.getInstance().setDeviceResultStateListener(new Shield.DeviceResultStateListener() {
133
- @Override
134
- public void isReady() {
135
- // Emit an event when the device is ready
136
- WritableMap params = Arguments.createMap();
137
- params.putString("status", "isSDKReady");
138
- reactContext
139
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
140
- .emit("device_result_state", params);
141
- }
142
- });
235
+ public void sendAttributes(String screenName, ReadableMap json) {
236
+ if (shield == null) return;
237
+
238
+ Map<String, String> data = toStringMap(json);
239
+
240
+ shield.sendAttributesWithCallback(screenName, data, result -> {
241
+ if (result instanceof Result.Failure) {
242
+ ShieldError error = ((Result.Failure<String>) result).getError();
243
+ emitEvent("error", error.getErrorMessage());
143
244
  }
144
- }, 1500);
245
+ });
145
246
  }
146
247
 
248
+ @ReactMethod
249
+ public void sendAttributesWithCallback(
250
+ String screenName,
251
+ ReadableMap json,
252
+ Callback successCallback,
253
+ Callback errorCallback) {
254
+ if (shield == null) {
255
+ errorCallback.invoke("Shield SDK is not initialized.");
256
+ return;
257
+ }
147
258
 
148
- @ReactMethod(isBlockingSynchronousMethod = true)
149
- public String getSessionId() {
150
- return Shield.getInstance().getSessionId();
259
+ Map<String, String> data = toStringMap(json);
260
+ shield.sendAttributesWithCallback(screenName, data, result ->
261
+ new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
262
+ if (result instanceof Result.Success) {
263
+ successCallback.invoke(true);
264
+ } else if (result instanceof Result.Failure) {
265
+ ShieldError error = ((Result.Failure<String>) result).getError();
266
+ String errorMessage = error != null
267
+ ? error.getErrorMessage()
268
+ : "Failed to send attributes.";
269
+ errorCallback.invoke(errorMessage);
270
+ }
271
+ })
272
+ );
151
273
  }
152
274
 
153
- @Override
154
- public void onSuccess(@Nullable JSONObject jsonObject) {
155
- if (jsonObject != null) {
156
- reactContext
157
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
158
- .emit("success", jsonObject.toString());
275
+ // =========================================================================
276
+ // Send device signature
277
+ //
278
+ // Triggers a device signature computation for the given screen name.
279
+ // On success the SDK calls back with the session ID (String); the latest
280
+ // device result JSONObject is then retrieved via shield.getLatestDeviceResult().
281
+ // =========================================================================
282
+
283
+ @ReactMethod
284
+ public void sendDeviceSignature(
285
+ String screenName,
286
+ Callback successCallback,
287
+ Callback errorCallback) {
288
+ if (shield == null) {
289
+ errorCallback.invoke("Shield SDK is not initialized.");
290
+ return;
159
291
  }
292
+
293
+ shield.sendDeviceSignatureWithCallback(screenName, result ->
294
+ new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
295
+ if (result instanceof Result.Success) {
296
+ JSONObject json = shield.getLatestDeviceResult();
297
+ successCallback.invoke(json != null ? toWritableMap(json) : null);
298
+ } else if (result instanceof Result.Failure) {
299
+ ShieldError error = ((Result.Failure<String>) result).getError();
300
+ errorCallback.invoke(error != null
301
+ ? error.getErrorMessage()
302
+ : "Failed to send device signature.");
303
+ }
304
+ })
305
+ );
160
306
  }
161
307
 
308
+ // =========================================================================
309
+ // RCTEventEmitter stubs — required for compatibility on both architectures
310
+ // =========================================================================
311
+
312
+ @ReactMethod
313
+ public void addListener(String eventName) {}
314
+
315
+ @ReactMethod
316
+ public void removeListeners(double count) {}
317
+
318
+ // =========================================================================
319
+ // Module teardown
320
+ // =========================================================================
321
+
162
322
  @Override
163
- public void onFailure(@Nullable ShieldException e) {
164
- if (e != null) {
165
- reactContext
166
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
167
- .emit("error", e.getLocalizedMessage());
168
- }
323
+ public void invalidate() {
324
+ // Do not call super.invalidate() — ReactContextBaseJavaModule does not
325
+ // declare invalidate() as a concrete method in older React Native versions
326
+ // (it is only on the NativeModule interface), so super.invalidate() fails
327
+ // to compile. Our own cleanup is all that is needed here.
328
+ shield = null;
329
+ }
330
+
331
+ // =========================================================================
332
+ // Internal helpers
333
+ // =========================================================================
169
334
 
335
+ private void emitEvent(String eventName, Object data) {
336
+ reactContext
337
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
338
+ .emit(eventName, data);
170
339
  }
171
340
 
172
- @ReactMethod
173
- public void sendAttributes(String screenName, ReadableMap json) {
174
- HashMap<String, String> data = new HashMap<String, String>();
341
+ private Map<String, String> toStringMap(ReadableMap json) {
342
+ Map<String, String> data = new HashMap<>();
175
343
  for (Map.Entry<String, Object> entry : json.toHashMap().entrySet()) {
176
- data.put(entry.getKey(), (String) entry.getValue());
344
+ if (entry.getValue() instanceof String) {
345
+ data.put(entry.getKey(), (String) entry.getValue());
346
+ }
177
347
  }
178
- Shield.getInstance().sendAttributes(screenName, data);
348
+ return data;
179
349
  }
180
350
 
181
- @ReactMethod
182
- public void getLatestDeviceResult(Callback successCallback, Callback errorCallback) {
183
- Shield shieldInstance = Shield.getInstance();
184
- JSONObject deviceResult = shieldInstance.getLatestDeviceResult();
185
-
186
- if (deviceResult != null) {
187
- successCallback.invoke(deviceResult.toString());
351
+ private WritableMap toWritableMap(JSONObject jsonObject) {
352
+ WritableMap map = Arguments.createMap();
353
+ Iterator<String> keys = jsonObject.keys();
354
+ while (keys.hasNext()) {
355
+ String key = keys.next();
356
+ putValueInMap(map, key, jsonObject.opt(key));
357
+ }
358
+ return map;
359
+ }
360
+
361
+ private WritableArray toWritableArray(JSONArray jsonArray) {
362
+ WritableArray array = Arguments.createArray();
363
+ for (int i = 0; i < jsonArray.length(); i++) {
364
+ pushValueInArray(array, jsonArray.opt(i));
365
+ }
366
+ return array;
367
+ }
368
+
369
+ private void putValueInMap(WritableMap map, String key, Object value) {
370
+ if (value == null || value == JSONObject.NULL) {
371
+ map.putNull(key);
372
+ } else if (value instanceof Boolean) {
373
+ map.putBoolean(key, (Boolean) value);
374
+ } else if (value instanceof Number) {
375
+ map.putDouble(key, ((Number) value).doubleValue());
376
+ } else if (value instanceof JSONObject) {
377
+ map.putMap(key, toWritableMap((JSONObject) value));
378
+ } else if (value instanceof JSONArray) {
379
+ map.putArray(key, toWritableArray((JSONArray) value));
188
380
  } else {
189
- ShieldException error = shieldInstance.getResponseError();
190
- String errorMessage = (error != null) ? error.getMessage() : "unknown error";
191
- errorCallback.invoke(errorMessage);
381
+ map.putString(key, String.valueOf(value));
192
382
  }
193
- }
194
- @ReactMethod
195
- public void addListener(String eventName) {
196
- // Code to handle adding listeners
197
383
  }
198
-
199
- @ReactMethod
200
- public void removeListeners(int count) {
201
- // Code to handle removing listeners
384
+
385
+ private void pushValueInArray(WritableArray array, Object value) {
386
+ if (value == null || value == JSONObject.NULL) {
387
+ array.pushNull();
388
+ } else if (value instanceof Boolean) {
389
+ array.pushBoolean((Boolean) value);
390
+ } else if (value instanceof Number) {
391
+ array.pushDouble(((Number) value).doubleValue());
392
+ } else if (value instanceof JSONObject) {
393
+ array.pushMap(toWritableMap((JSONObject) value));
394
+ } else if (value instanceof JSONArray) {
395
+ array.pushArray(toWritableArray((JSONArray) value));
396
+ } else {
397
+ array.pushString(String.valueOf(value));
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Maps JS logLevel integer → Shield SDK LogLevel enum.
403
+ *
404
+ * JS LogLevel enum values (src/index.tsx):
405
+ * LogLevelVerbose = 4 → LogLevel.VERBOSE
406
+ * LogLevelDebug = 3 → LogLevel.DEBUG
407
+ * LogLevelInfo = 2 → LogLevel.INFO
408
+ * LogLevelNone = 1 → LogLevel.NONE (default)
409
+ */
410
+ private LogLevel getLogLevelFromInt(int logLevel) {
411
+ switch (logLevel) {
412
+ case 4: return LogLevel.VERBOSE;
413
+ case 3: return LogLevel.DEBUG;
414
+ case 2: return LogLevel.INFO;
415
+ default: return LogLevel.NONE;
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Maps JS environmentInfo integer → Shield SDK Environment enum.
421
+ *
422
+ * JS EnvironmentInfo enum values (src/index.tsx):
423
+ * EnvironmentProd = 0 → Environment.PROD (default)
424
+ * EnvironmentDev = 1 → Environment.DEV
425
+ * EnvironmentStaging = 2 → Environment.STAGING
426
+ */
427
+ private Environment getEnvironmentFromInt(int environment) {
428
+ switch (environment) {
429
+ case 1: return Environment.DEV;
430
+ case 2: return Environment.STAGING;
431
+ default: return Environment.PROD;
432
+ }
202
433
  }
203
434
  }