react-native-shield-full-plugin 2.3.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 (30) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +237 -0
  3. package/android/build.gradle +109 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +5 -0
  6. package/android/src/main/java/com/shieldfraudplugin/ShieldFraudPluginModule.java +441 -0
  7. package/android/src/main/java/com/shieldfraudplugin/ShieldFraudPluginPackage.java +78 -0
  8. package/android/src/newarch/java/com/shieldfraudplugin/ShieldFraudPluginSpec.java +41 -0
  9. package/android/src/oldarch/java/com/shieldfraudplugin/ShieldFraudPluginSpec.java +36 -0
  10. package/ios/ShieldFraudPlugin.h +24 -0
  11. package/ios/ShieldFraudPlugin.mm +295 -0
  12. package/ios/ShieldFraudPlugin.xcodeproj/project.pbxproj +282 -0
  13. package/ios/ShieldFraudPlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  14. package/lib/commonjs/NativeShieldFraudPlugin.js +15 -0
  15. package/lib/commonjs/NativeShieldFraudPlugin.js.map +1 -0
  16. package/lib/commonjs/index.js +256 -0
  17. package/lib/commonjs/index.js.map +1 -0
  18. package/lib/module/NativeShieldFraudPlugin.js +10 -0
  19. package/lib/module/NativeShieldFraudPlugin.js.map +1 -0
  20. package/lib/module/index.js +253 -0
  21. package/lib/module/index.js.map +1 -0
  22. package/lib/typescript/src/NativeShieldFraudPlugin.d.ts +53 -0
  23. package/lib/typescript/src/NativeShieldFraudPlugin.d.ts.map +1 -0
  24. package/lib/typescript/src/index.d.ts +143 -0
  25. package/lib/typescript/src/index.d.ts.map +1 -0
  26. package/package.json +169 -0
  27. package/react-native-shield-fraud-plugin.podspec +29 -0
  28. package/react-native-shield-full-plugin.podspec +29 -0
  29. package/src/NativeShieldFraudPlugin.ts +87 -0
  30. package/src/index.tsx +334 -0
@@ -0,0 +1,441 @@
1
+ package com.shieldfraudplugin;
2
+
3
+ import android.app.Application;
4
+
5
+ import androidx.annotation.NonNull;
6
+ import androidx.annotation.Nullable;
7
+
8
+ import com.facebook.react.bridge.Arguments;
9
+ import com.facebook.react.bridge.Callback;
10
+ import com.facebook.react.bridge.Promise;
11
+ import com.facebook.react.bridge.ReactApplicationContext;
12
+ import com.facebook.react.bridge.ReactMethod;
13
+ import com.facebook.react.bridge.ReadableMap;
14
+ import com.facebook.react.bridge.WritableArray;
15
+ import com.facebook.react.bridge.WritableMap;
16
+ import com.facebook.react.module.annotations.ReactModule;
17
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
18
+
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;
24
+ import com.shield.android.Shield;
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;
30
+ import com.shield.android.ShieldUserData;
31
+
32
+ import org.json.JSONArray;
33
+ import org.json.JSONObject;
34
+
35
+ import java.util.HashMap;
36
+ import java.util.Iterator;
37
+ import java.util.Map;
38
+
39
+ /**
40
+ * ShieldFraudPlugin native module — Shield SDK 2.x (pure Java)
41
+ *
42
+ * SDK 2.x migration summary
43
+ * ─────────────────────────
44
+ * 1. Initialisation : Shield.Builder → ShieldConfig + ShieldFactory
45
+ * 2. Instance access : Shield.getInstance() → stored `shield` member
46
+ * 3. Session ID : Shield.getInstance().getSessionId() → shield.getSessionId()
47
+ * 4. Device results : setDeviceResultStateListener(callback)
48
+ * → ShieldFactory.createShieldWithCallback (SHIELD Sentinel)
49
+ * 5. sendAttributes : void return
50
+ * → shield.sendAttributesWithCallback (Result<String> callback)
51
+ * 6. LogLevel enum : Shield.LogLevel.VERBOSE/DEBUG/INFO/NONE
52
+ * → LogLevel.VERBOSE / DEBUG / INFO / NONE
53
+ * 7. Environment enum : String constants ENVIRONMENT_PROD / DEV / STAGING
54
+ * → Environment.PROD / DEV / STAGING
55
+ *
56
+ * Architecture segmentation
57
+ * ─────────────────────────
58
+ * Handled by Gradle source sets (not #ifdef).
59
+ * src/oldarch/ShieldFraudPluginSpec.java → extends ReactContextBaseJavaModule
60
+ * src/newarch/ShieldFraudPluginSpec.java → extends NativeShieldFraudPluginSpec (codegen)
61
+ * This module always extends ShieldFraudPluginSpec.
62
+ */
63
+ @ReactModule(name = ShieldFraudPluginModule.NAME)
64
+ public class ShieldFraudPluginModule extends com.shieldfraudplugin.ShieldFraudPluginSpec {
65
+
66
+ public static final String NAME = "ShieldFraudPlugin";
67
+
68
+ private final ReactApplicationContext reactContext;
69
+
70
+ /**
71
+ * Stored Shield instance — created once by ShieldFactory in initShield().
72
+ * All subsequent calls use this reference instead of a singleton accessor.
73
+ */
74
+ @Nullable
75
+ private Shield shield = null;
76
+
77
+
78
+ public ShieldFraudPluginModule(ReactApplicationContext reactContext) {
79
+ super(reactContext);
80
+ this.reactContext = reactContext;
81
+ }
82
+
83
+ @Override
84
+ @NonNull
85
+ public String getName() {
86
+ return NAME;
87
+ }
88
+
89
+ // =========================================================================
90
+ // Synchronous state queries
91
+ // =========================================================================
92
+
93
+ @ReactMethod(isBlockingSynchronousMethod = true)
94
+ public boolean isShieldInitialized() {
95
+ return shield != null;
96
+ }
97
+
98
+ @ReactMethod(isBlockingSynchronousMethod = true)
99
+ public String getSessionId() {
100
+ return shield != null ? shield.getSessionId() : "";
101
+ }
102
+
103
+ // =========================================================================
104
+ // SDK initialisation (Shield SDK 2.x — ShieldConfig + ShieldFactory)
105
+ //
106
+ // createShieldWithCallback both initialises the SDK and acts as the
107
+ // SHIELD Sentinel callback — it fires on the initial fingerprint and on
108
+ // every subsequent device intelligence update during the session.
109
+ //
110
+ // logLevel / environmentInfo arrive as `double` from both architectures:
111
+ // • Old Arch — JS bridge maps JS numbers to double.
112
+ // • New Arch — codegen generates double for TypeScript number params.
113
+ // =========================================================================
114
+
115
+ @ReactMethod
116
+ public void initShield(
117
+ String siteID,
118
+ String secretKey,
119
+ boolean isOptimizedListener,
120
+ @Nullable ReadableMap blockedDialog,
121
+ double logLevel,
122
+ double environmentInfo,
123
+ boolean blockScreenRecording,
124
+ Promise promise) {
125
+
126
+ if (isShieldInitialized()) {
127
+ promise.resolve(null);
128
+ return;
129
+ }
130
+
131
+ Application application = (Application) reactContext.getApplicationContext();
132
+
133
+ ShieldConfig config = new ShieldConfig(siteID, secretKey);
134
+ config.setLogLevel(getLogLevelFromInt((int) logLevel));
135
+ config.setEnvironment(getEnvironmentFromInt((int) environmentInfo));
136
+
137
+ if (blockedDialog != null) {
138
+ String title = blockedDialog.hasKey("title") ? blockedDialog.getString("title") : null;
139
+ String body = blockedDialog.hasKey("body") ? blockedDialog.getString("body") : null;
140
+ config.setBlockedDialog(new BlockedDialog(title, body));
141
+ }
142
+
143
+ // blockScreenRecording is Android-only. JS always sends a real boolean
144
+ // (true/false, never null) so the Old Arch bridge can safely read it as
145
+ // a primitive. Defaults to false when the user doesn't set it in Config.
146
+ config.setBlockScreenRecording(blockScreenRecording);
147
+
148
+ if (isOptimizedListener) {
149
+ // SDK 2.x: createShieldWithCallback initialises + acts as the Sentinel.
150
+ // Unlike 1.x (which required a separate setDeviceResultStateListener() call
151
+ // on the SDK), 2.x fires this callback automatically when device intelligence
152
+ // is ready and on every subsequent risk-profile change.
153
+ // The callback may arrive on a background thread — always post to main looper.
154
+ shield = ShieldFactory.createShieldWithCallback(
155
+ application,
156
+ config,
157
+ result -> new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
158
+ if (result instanceof Result.Success) {
159
+ DeviceIntelligence di =
160
+ ((Result.Success<DeviceIntelligence>) result).getData();
161
+ JSONObject json = di != null ? di.getData() : null;
162
+ emitEvent("success", json != null ? json.toString() : null);
163
+ } else if (result instanceof Result.Failure) {
164
+ ShieldError error =
165
+ ((Result.Failure<DeviceIntelligence>) result).getError();
166
+ emitEvent("error", error.getErrorMessage());
167
+ }
168
+ }));
169
+ } else {
170
+ // createShield = initialise without the real-time Sentinel callback.
171
+ // Use getLatestDeviceResult() to poll on demand instead.
172
+ shield = ShieldFactory.createShield(application, config);
173
+ }
174
+
175
+ promise.resolve(null);
176
+ }
177
+
178
+ // =========================================================================
179
+ // Cross-platform metadata
180
+ // =========================================================================
181
+
182
+ @ReactMethod
183
+ public void setCrossPlatformParameters(String crossPlatformName, String crossPlatformVersion) {
184
+ ShieldCrossPlatformParams params =
185
+ new ShieldCrossPlatformParams(crossPlatformName, crossPlatformVersion);
186
+ ShieldCrossPlatformHelper.setCrossPlatformParameters(params);
187
+ }
188
+
189
+ // =========================================================================
190
+ // setDeviceResultStateListener — no-op stub (SDK 2.x)
191
+ //
192
+ // SDK 1.x required a manual subscription call + Handler.postDelayed workaround.
193
+ // SDK 2.x fires device result events automatically via createShieldWithCallback
194
+ // when isOptimizedListener == true — no subscription call needed.
195
+ //
196
+ // The method is kept as a no-op stub so the shared TypeScript codegen spec
197
+ // and iOS (which still calls the SDK method) remain unaffected.
198
+ // =========================================================================
199
+
200
+ @ReactMethod
201
+ public void setDeviceResultStateListener() {
202
+ // No-op on Android SDK 2.x — device result events are delivered
203
+ // automatically through the createShieldWithCallback Sentinel.
204
+ }
205
+
206
+ // =========================================================================
207
+ // Retrieve the latest device intelligence result
208
+ //
209
+ // Returns Shield SDK's latest device result payload.
210
+ // =========================================================================
211
+
212
+ @ReactMethod
213
+ public void getLatestDeviceResult(Callback successCallback, Callback errorCallback) {
214
+ if (shield == null) {
215
+ errorCallback.invoke("Shield SDK is not initialized.");
216
+ return;
217
+ }
218
+
219
+ JSONObject json = shield.getLatestDeviceResult();
220
+ if (json == null) {
221
+ errorCallback.invoke("No device result available yet.");
222
+ return;
223
+ }
224
+
225
+ successCallback.invoke(toWritableMap(json));
226
+ }
227
+
228
+ // =========================================================================
229
+ // Send user / screen attributes (Shield SDK 2.x — sendAttributesWithCallback)
230
+ //
231
+ // sendAttributesWithCallback delivers a Result<String> where the String
232
+ // is the sessionId on success.
233
+ // =========================================================================
234
+
235
+ @ReactMethod
236
+ public void sendAttributes(String screenName, ReadableMap json) {
237
+ if (shield == null) return;
238
+
239
+ Map<String, String> data = toStringMap(json);
240
+
241
+ shield.sendAttributesWithCallback(screenName, data, result -> {
242
+ if (result instanceof Result.Failure) {
243
+ ShieldError error = ((Result.Failure<String>) result).getError();
244
+ emitEvent("error", error.getErrorMessage());
245
+ }
246
+ });
247
+ }
248
+
249
+ @ReactMethod
250
+ public void sendAttributesWithCallback(
251
+ String screenName,
252
+ ReadableMap json,
253
+ Callback successCallback,
254
+ Callback errorCallback) {
255
+ if (shield == null) {
256
+ errorCallback.invoke("Shield SDK is not initialized.");
257
+ return;
258
+ }
259
+
260
+ Map<String, String> data = toStringMap(json);
261
+ shield.sendAttributesWithCallback(screenName, data, result ->
262
+ new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
263
+ if (result instanceof Result.Success) {
264
+ successCallback.invoke(true);
265
+ } else if (result instanceof Result.Failure) {
266
+ ShieldError error = ((Result.Failure<String>) result).getError();
267
+ String errorMessage = error != null
268
+ ? error.getErrorMessage()
269
+ : "Failed to send attributes.";
270
+ errorCallback.invoke(errorMessage);
271
+ }
272
+ })
273
+ );
274
+ }
275
+
276
+ // =========================================================================
277
+ // Send device signature
278
+ //
279
+ // Triggers a device signature computation for the given screen name.
280
+ // On success the SDK calls back with the session ID (String); the latest
281
+ // device result JSONObject is then retrieved via shield.getLatestDeviceResult().
282
+ // =========================================================================
283
+
284
+ @ReactMethod
285
+ public void sendDeviceSignature(
286
+ String screenName,
287
+ @Nullable String userId,
288
+ Callback successCallback,
289
+ Callback errorCallback) {
290
+ if (shield == null) {
291
+ errorCallback.invoke("Shield SDK is not initialized.");
292
+ return;
293
+ }
294
+
295
+ ShieldUserData userData = new ShieldUserData(screenName);
296
+ if (userId != null) {
297
+ userData.setUserId(userId);
298
+ }
299
+
300
+ shield.sendDeviceSignatureWithCallback(userData, result ->
301
+ new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
302
+ if (result instanceof Result.Success) {
303
+ JSONObject json = shield.getLatestDeviceResult();
304
+ successCallback.invoke(json != null ? toWritableMap(json) : null);
305
+ } else if (result instanceof Result.Failure) {
306
+ ShieldError error = ((Result.Failure<String>) result).getError();
307
+ errorCallback.invoke(error != null
308
+ ? error.getErrorMessage()
309
+ : "Failed to send device signature.");
310
+ }
311
+ })
312
+ );
313
+ }
314
+
315
+ // =========================================================================
316
+ // RCTEventEmitter stubs — required for compatibility on both architectures
317
+ // =========================================================================
318
+
319
+ @ReactMethod
320
+ public void addListener(String eventName) {}
321
+
322
+ @ReactMethod
323
+ public void removeListeners(double count) {}
324
+
325
+ // =========================================================================
326
+ // Module teardown
327
+ // =========================================================================
328
+
329
+ @Override
330
+ public void invalidate() {
331
+ // Do not call super.invalidate() — ReactContextBaseJavaModule does not
332
+ // declare invalidate() as a concrete method in older React Native versions
333
+ // (it is only on the NativeModule interface), so super.invalidate() fails
334
+ // to compile. Our own cleanup is all that is needed here.
335
+ shield = null;
336
+ }
337
+
338
+ // =========================================================================
339
+ // Internal helpers
340
+ // =========================================================================
341
+
342
+ private void emitEvent(String eventName, Object data) {
343
+ reactContext
344
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
345
+ .emit(eventName, data);
346
+ }
347
+
348
+ private Map<String, String> toStringMap(ReadableMap json) {
349
+ Map<String, String> data = new HashMap<>();
350
+ for (Map.Entry<String, Object> entry : json.toHashMap().entrySet()) {
351
+ if (entry.getValue() instanceof String) {
352
+ data.put(entry.getKey(), (String) entry.getValue());
353
+ }
354
+ }
355
+ return data;
356
+ }
357
+
358
+ private WritableMap toWritableMap(JSONObject jsonObject) {
359
+ WritableMap map = Arguments.createMap();
360
+ Iterator<String> keys = jsonObject.keys();
361
+ while (keys.hasNext()) {
362
+ String key = keys.next();
363
+ putValueInMap(map, key, jsonObject.opt(key));
364
+ }
365
+ return map;
366
+ }
367
+
368
+ private WritableArray toWritableArray(JSONArray jsonArray) {
369
+ WritableArray array = Arguments.createArray();
370
+ for (int i = 0; i < jsonArray.length(); i++) {
371
+ pushValueInArray(array, jsonArray.opt(i));
372
+ }
373
+ return array;
374
+ }
375
+
376
+ private void putValueInMap(WritableMap map, String key, Object value) {
377
+ if (value == null || value == JSONObject.NULL) {
378
+ map.putNull(key);
379
+ } else if (value instanceof Boolean) {
380
+ map.putBoolean(key, (Boolean) value);
381
+ } else if (value instanceof Number) {
382
+ map.putDouble(key, ((Number) value).doubleValue());
383
+ } else if (value instanceof JSONObject) {
384
+ map.putMap(key, toWritableMap((JSONObject) value));
385
+ } else if (value instanceof JSONArray) {
386
+ map.putArray(key, toWritableArray((JSONArray) value));
387
+ } else {
388
+ map.putString(key, String.valueOf(value));
389
+ }
390
+ }
391
+
392
+ private void pushValueInArray(WritableArray array, Object value) {
393
+ if (value == null || value == JSONObject.NULL) {
394
+ array.pushNull();
395
+ } else if (value instanceof Boolean) {
396
+ array.pushBoolean((Boolean) value);
397
+ } else if (value instanceof Number) {
398
+ array.pushDouble(((Number) value).doubleValue());
399
+ } else if (value instanceof JSONObject) {
400
+ array.pushMap(toWritableMap((JSONObject) value));
401
+ } else if (value instanceof JSONArray) {
402
+ array.pushArray(toWritableArray((JSONArray) value));
403
+ } else {
404
+ array.pushString(String.valueOf(value));
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Maps JS logLevel integer → Shield SDK LogLevel enum.
410
+ *
411
+ * JS LogLevel enum values (src/index.tsx):
412
+ * LogLevelVerbose = 4 → LogLevel.VERBOSE
413
+ * LogLevelDebug = 3 → LogLevel.DEBUG
414
+ * LogLevelInfo = 2 → LogLevel.INFO
415
+ * LogLevelNone = 1 → LogLevel.NONE (default)
416
+ */
417
+ private LogLevel getLogLevelFromInt(int logLevel) {
418
+ switch (logLevel) {
419
+ case 4: return LogLevel.VERBOSE;
420
+ case 3: return LogLevel.DEBUG;
421
+ case 2: return LogLevel.INFO;
422
+ default: return LogLevel.NONE;
423
+ }
424
+ }
425
+
426
+ /**
427
+ * Maps JS environmentInfo integer → Shield SDK Environment enum.
428
+ *
429
+ * JS EnvironmentInfo enum values (src/index.tsx):
430
+ * EnvironmentProd = 0 → Environment.PROD (default)
431
+ * EnvironmentDev = 1 → Environment.DEV
432
+ * EnvironmentStaging = 2 → Environment.STAGING
433
+ */
434
+ private Environment getEnvironmentFromInt(int environment) {
435
+ switch (environment) {
436
+ case 1: return Environment.DEV;
437
+ case 2: return Environment.STAGING;
438
+ default: return Environment.PROD;
439
+ }
440
+ }
441
+ }
@@ -0,0 +1,78 @@
1
+ package com.shieldfraudplugin;
2
+
3
+ import androidx.annotation.NonNull;
4
+
5
+ import com.facebook.react.TurboReactPackage;
6
+ import com.facebook.react.bridge.NativeModule;
7
+ import com.facebook.react.bridge.ReactApplicationContext;
8
+ import com.facebook.react.module.model.ReactModuleInfo;
9
+ import com.facebook.react.module.model.ReactModuleInfoProvider;
10
+
11
+ import java.util.HashMap;
12
+ import java.util.Map;
13
+
14
+ /**
15
+ * Package registration for ShieldFraudPlugin — works on both architectures.
16
+ *
17
+ * Why TurboReactPackage instead of ReactPackage
18
+ * ─────────────────────────────────────────────
19
+ * TurboReactPackage is a drop-in replacement for the legacy ReactPackage that
20
+ * adds support for Turbo Modules (New Architecture) while remaining fully
21
+ * backward-compatible with the Old Architecture bridge.
22
+ *
23
+ * On Old Architecture the isTurboModule flag in ReactModuleInfo is simply
24
+ * ignored and the module is loaded via the bridge as normal.
25
+ * On New Architecture the flag tells the runtime to resolve the module through
26
+ * the Turbo Module system (JSI) instead of the bridge.
27
+ *
28
+ * The isTurboModule flag is driven by BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
29
+ * which build.gradle sets to true/false based on the newArchEnabled property:
30
+ *
31
+ * buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED",
32
+ * isNewArchitectureEnabled().toString()
33
+ */
34
+ public class ShieldFraudPluginPackage extends TurboReactPackage {
35
+
36
+ // =========================================================================
37
+ // Module instantiation
38
+ // =========================================================================
39
+
40
+ @Override
41
+ public NativeModule getModule(@NonNull String name,
42
+ @NonNull ReactApplicationContext reactContext) {
43
+ if (name.equals(ShieldFraudPluginModule.NAME)) {
44
+ return new ShieldFraudPluginModule(reactContext);
45
+ }
46
+ return null;
47
+ }
48
+
49
+ // =========================================================================
50
+ // Module metadata
51
+ //
52
+ // ReactModuleInfoProvider returns the metadata the runtime needs to decide
53
+ // how to load the module:
54
+ // • Old Arch → isTurboModule=false → loaded via JS bridge
55
+ // • New Arch → isTurboModule=true → loaded via JSI (Turbo Module)
56
+ // =========================================================================
57
+
58
+ @Override
59
+ public ReactModuleInfoProvider getReactModuleInfoProvider() {
60
+ return () -> {
61
+ final boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
62
+ Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
63
+ moduleInfos.put(
64
+ ShieldFraudPluginModule.NAME,
65
+ new ReactModuleInfo(
66
+ ShieldFraudPluginModule.NAME, // name
67
+ ShieldFraudPluginModule.NAME, // className
68
+ false, // canOverrideExistingModule
69
+ false, // needsEagerInit
70
+ false, // hasConstants
71
+ false, // isCxxModule
72
+ isTurboModule // isTurboModule
73
+ )
74
+ );
75
+ return moduleInfos;
76
+ };
77
+ }
78
+ }
@@ -0,0 +1,41 @@
1
+ package com.shieldfraudplugin;
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext;
4
+
5
+ /**
6
+ * New Architecture base class for ShieldFraudPluginModule.
7
+ *
8
+ * Selected by Gradle when newArchEnabled=true.
9
+ * Extends NativeShieldFraudPluginSpec — the abstract class generated by React
10
+ * Native's codegen from the TypeScript spec at src/NativeShieldFraudPlugin.ts.
11
+ *
12
+ * How the source-set swap works
13
+ * ─────────────────────────────
14
+ * build.gradle adds either src/oldarch or src/newarch to the Java source
15
+ * directories depending on the newArchEnabled project property:
16
+ *
17
+ * sourceSets {
18
+ * main {
19
+ * if (isNewArchitectureEnabled()) {
20
+ * java.srcDirs += ['src/newarch'] // this file is included
21
+ * } else {
22
+ * java.srcDirs += ['src/oldarch'] // this file is excluded
23
+ * }
24
+ * }
25
+ * }
26
+ *
27
+ * ShieldFraudPluginModule (in src/main/) always extends ShieldFraudPluginSpec,
28
+ * so it compiles correctly regardless of which shim is selected.
29
+ *
30
+ * NativeShieldFraudPluginSpec is generated at build time by the React Native
31
+ * Gradle plugin (com.facebook.react) when New Architecture is enabled.
32
+ * It declares all methods from the TypeScript spec as abstract — this shim
33
+ * exists purely to bridge the inheritance chain so ShieldFraudPluginModule
34
+ * does not need to import the codegen class directly.
35
+ */
36
+ abstract class ShieldFraudPluginSpec extends NativeShieldFraudPluginSpec {
37
+
38
+ protected ShieldFraudPluginSpec(ReactApplicationContext context) {
39
+ super(context);
40
+ }
41
+ }
@@ -0,0 +1,36 @@
1
+ package com.shieldfraudplugin;
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext;
4
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
5
+
6
+ /**
7
+ * Old Architecture base class for ShieldFraudPluginModule.
8
+ *
9
+ * Selected by Gradle when newArchEnabled=false (the default).
10
+ * Extends ReactContextBaseJavaModule — the standard base class for modules
11
+ * that communicate with JS over the legacy bridge.
12
+ *
13
+ * How the source-set swap works
14
+ * ─────────────────────────────
15
+ * build.gradle adds either src/oldarch or src/newarch to the Java source
16
+ * directories depending on the newArchEnabled project property:
17
+ *
18
+ * sourceSets {
19
+ * main {
20
+ * if (isNewArchitectureEnabled()) {
21
+ * java.srcDirs += ['src/newarch'] // this file is excluded
22
+ * } else {
23
+ * java.srcDirs += ['src/oldarch'] // this file is included
24
+ * }
25
+ * }
26
+ * }
27
+ *
28
+ * ShieldFraudPluginModule (in src/main/) always extends ShieldFraudPluginSpec,
29
+ * so it compiles correctly regardless of which shim is selected.
30
+ */
31
+ abstract class ShieldFraudPluginSpec extends ReactContextBaseJavaModule {
32
+
33
+ protected ShieldFraudPluginSpec(ReactApplicationContext context) {
34
+ super(context);
35
+ }
36
+ }
@@ -0,0 +1,24 @@
1
+
2
+ #ifdef RCT_NEW_ARCH_ENABLED
3
+ // New Architecture: conform to the codegen-generated spec protocol.
4
+ // RCTEventEmitter is still used as the base so that sendEventWithName: works.
5
+ #import <React/RCTEventEmitter.h>
6
+ #import "RNShieldFraudPluginSpec.h"
7
+
8
+ // Forward-declare the protocol — the full definition is imported in the .mm file.
9
+ // A forward declaration is sufficient for the @interface conformance declaration.
10
+ @protocol DeviceShieldCallback;
11
+
12
+ @interface ShieldFraudPlugin : RCTEventEmitter <NativeShieldFraudPluginSpec, DeviceShieldCallback>
13
+ #else
14
+ // Old Architecture: standard bridge module + event emitter.
15
+ #import <React/RCTBridgeModule.h>
16
+ #import <React/RCTEventEmitter.h>
17
+
18
+ // Forward-declare the protocol — full definition imported in the .mm file.
19
+ @protocol DeviceShieldCallback;
20
+
21
+ @interface ShieldFraudPlugin : RCTEventEmitter <RCTBridgeModule, DeviceShieldCallback>
22
+ #endif
23
+
24
+ @end