cm-sdk-react-native-v3 3.8.0 → 3.10.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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # ``consentmanager CMP SDK v3.8.0``
1
+ # ``consentmanager CMP SDK v3.10.0``
2
2
 
3
3
  # cm-sdk-react-native-v3
4
4
 
@@ -89,5 +89,5 @@ dependencies {
89
89
  implementation "androidx.lifecycle:lifecycle-common-java8:2.6.1"
90
90
 
91
91
  // consentmanager's underlying native Android SDK dependency
92
- implementation "net.consentmanager.sdkv3:cmsdkv3:3.8.0"
92
+ implementation "net.consentmanager.sdkv3:cmsdkv3:3.10.0"
93
93
  }
@@ -4,6 +4,8 @@ import android.os.Handler
4
4
  import android.os.Looper
5
5
  import android.util.Log
6
6
  import android.app.Activity
7
+ import android.webkit.CookieManager
8
+ import android.webkit.WebStorage
7
9
  import com.facebook.react.bridge.Arguments
8
10
  import com.facebook.react.bridge.LifecycleEventListener
9
11
  import com.facebook.react.bridge.Promise
@@ -24,7 +26,7 @@ import net.consentmanager.cm_sdk_android_v3.CMPManagerDelegate
24
26
  import net.consentmanager.cm_sdk_android_v3.ConsentLayerUIConfig
25
27
  import net.consentmanager.cm_sdk_android_v3.ConsentStatus
26
28
  import net.consentmanager.cm_sdk_android_v3.UrlConfig
27
- import net.consentmanager.cm_sdk_android_v3.UserConsentStatus
29
+ import net.consentmanager.cm_sdk_android_v3.UserChoiceStatus
28
30
 
29
31
  class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
30
32
  ReactContextBaseJavaModule(reactContext), LifecycleEventListener, CMPManagerDelegate {
@@ -37,6 +39,7 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
37
39
  private var isInitialized = false
38
40
  private var storedATTStatus: Int = 0
39
41
  private var isWebViewConfigSet = false
42
+ private var automaticConsentUpdatesEnabled = true
40
43
 
41
44
 
42
45
  init {
@@ -113,11 +116,13 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
113
116
 
114
117
  this.webViewConfig = ConsentLayerUIConfig(
115
118
  position = position,
116
- backgroundStyle = ConsentLayerUIConfig.BackgroundStyle.dimmed(android.graphics.Color.BLACK, 0.5f),
119
+ backgroundStyle = mapBackgroundStyle(config),
117
120
  cornerRadius = cornerRadius,
118
121
  respectsSafeArea = if (config.hasKey("respectsSafeArea")) config.getBoolean("respectsSafeArea") else true,
119
122
  isCancelable = false,
120
- allowsOrientationChanges = if (config.hasKey("allowsOrientationChanges")) config.getBoolean("allowsOrientationChanges") else true
123
+ allowsOrientationChanges = if (config.hasKey("allowsOrientationChanges")) config.getBoolean("allowsOrientationChanges") else true,
124
+ darkMode = if (config.hasKey("darkMode")) config.getBoolean("darkMode") else false,
125
+ navigationBarColor = readOptionalColor(config, "navigationBarColor")
121
126
  )
122
127
  isWebViewConfigSet = true
123
128
 
@@ -135,9 +140,21 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
135
140
  val domain = config.getString("domain") ?: throw IllegalArgumentException("Missing 'domain'")
136
141
  val language = config.getString("language") ?: throw IllegalArgumentException("Missing 'language'")
137
142
  val appName = config.getString("appName") ?: throw IllegalArgumentException("Missing 'appName'")
143
+ val jsonConfig = if (config.hasKey("jsonConfig")) config.getString("jsonConfig") else null
138
144
  val noHash = if (config.hasKey("noHash")) config.getBoolean("noHash") else false
139
-
140
- this.urlConfig = UrlConfig(id, domain, language, appName, noHash = noHash)
145
+ val webViewConnectionTimeoutMillis = if (config.hasKey("webViewConnectionTimeoutMillis")) config.getDouble("webViewConnectionTimeoutMillis").toLong() else 3000L
146
+ val forceRegulation = if (config.hasKey("forceRegulation")) config.getString("forceRegulation") else null
147
+
148
+ this.urlConfig = UrlConfig(
149
+ id = id,
150
+ domain = domain,
151
+ language = language,
152
+ appName = appName,
153
+ jsonConfig = jsonConfig,
154
+ noHash = noHash,
155
+ webViewConnectionTimeoutMillis = webViewConnectionTimeoutMillis,
156
+ forceRegulation = forceRegulation
157
+ )
141
158
  // Ensure we initialize manager only once and with whatever webViewConfig is currently set
142
159
  if (!::cmpManager.isInitialized) {
143
160
  initializeCMPManager()
@@ -151,7 +168,7 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
151
168
  }
152
169
 
153
170
  private fun initializeCMPManager() {
154
- val activity = currentActivitySafe ?: throw IllegalStateException("Current activity is null")
171
+ val activity = currentActivitySafe ?: throw IllegalStateException("Current activity is null. Wait until the app is active before calling setUrlConfig().")
155
172
  Log.d("CmSdkReactNativeV3", "Initializing CMPManager with activity: $activity, delegate: $this")
156
173
 
157
174
  cmpManager = CMPManager.getInstance(
@@ -194,30 +211,34 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
194
211
  */
195
212
  @ReactMethod
196
213
  fun getUserStatus(promise: Promise) {
197
- try {
198
- val userStatus = cmpManager.getUserStatus()
199
- val result = Arguments.createMap().apply {
200
- putString("hasUserChoice", userStatus.hasUserChoice.toString())
201
- putString("tcf", userStatus.tcf)
202
- putString("addtlConsent", userStatus.addtlConsent)
203
- putString("regulation", userStatus.regulation)
204
-
205
- val vendorsMap = Arguments.createMap()
206
- userStatus.vendors.forEach { (vendorId, status) ->
207
- vendorsMap.putString(vendorId, status.toString())
208
- }
209
- putMap("vendors", vendorsMap)
214
+ withCmpManager(promise) { manager ->
215
+ try {
216
+ val userStatus = manager.getUserStatus()
217
+ val normalizedStatus = mapUserChoiceStatus(userStatus.hasUserChoice)
218
+ val result = Arguments.createMap().apply {
219
+ putString("status", normalizedStatus)
220
+ putString("hasUserChoice", normalizedStatus)
221
+ putString("tcf", userStatus.tcf)
222
+ putString("addtlConsent", userStatus.addtlConsent)
223
+ putString("regulation", userStatus.regulation)
224
+
225
+ val vendorsMap = Arguments.createMap()
226
+ userStatus.vendors.forEach { (vendorId, status) ->
227
+ vendorsMap.putString(vendorId, mapConsentStatus(status))
228
+ }
229
+ putMap("vendors", vendorsMap)
210
230
 
211
- val purposesMap = Arguments.createMap()
212
- userStatus.purposes.forEach { (purposeId, status) ->
213
- purposesMap.putString(purposeId, status.toString())
231
+ val purposesMap = Arguments.createMap()
232
+ userStatus.purposes.forEach { (purposeId, status) ->
233
+ purposesMap.putString(purposeId, mapConsentStatus(status))
234
+ }
235
+ putMap("purposes", purposesMap)
214
236
  }
215
- putMap("purposes", purposesMap)
216
- }
217
237
 
218
- promise.resolve(result)
219
- } catch (e: Exception) {
220
- promise.reject("ERROR", "Failed to get user status: ${e.message}", e)
238
+ promise.resolve(result)
239
+ } catch (e: Exception) {
240
+ promise.reject("ERROR", "Failed to get user status: ${e.message}", e)
241
+ }
221
242
  }
222
243
  }
223
244
 
@@ -231,11 +252,13 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
231
252
  */
232
253
  @ReactMethod
233
254
  fun getStatusForPurpose(purposeId: String, promise: Promise) {
234
- try {
235
- val status = cmpManager.getStatusForPurpose(purposeId)
236
- promise.resolve(status.toString())
237
- } catch (e: Exception) {
238
- promise.reject("ERROR", "Failed to get status for purpose: ${e.message}", e)
255
+ withCmpManager(promise) { manager ->
256
+ try {
257
+ val status = manager.getStatusForPurpose(purposeId)
258
+ promise.resolve(mapConsentStatus(status))
259
+ } catch (e: Exception) {
260
+ promise.reject("ERROR", "Failed to get status for purpose: ${e.message}", e)
261
+ }
239
262
  }
240
263
  }
241
264
 
@@ -244,11 +267,13 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
244
267
  */
245
268
  @ReactMethod
246
269
  fun getStatusForVendor(vendorId: String, promise: Promise) {
247
- try {
248
- val status = cmpManager.getStatusForVendor(vendorId)
249
- promise.resolve(status.toString())
250
- } catch (e: Exception) {
251
- promise.reject("ERROR", "Failed to get status for vendor: ${e.message}", e)
270
+ withCmpManager(promise) { manager ->
271
+ try {
272
+ val status = manager.getStatusForVendor(vendorId)
273
+ promise.resolve(mapConsentStatus(status))
274
+ } catch (e: Exception) {
275
+ promise.reject("ERROR", "Failed to get status for vendor: ${e.message}", e)
276
+ }
252
277
  }
253
278
  }
254
279
 
@@ -257,17 +282,19 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
257
282
  */
258
283
  @ReactMethod
259
284
  fun getGoogleConsentModeStatus(promise: Promise) {
260
- try {
261
- val consentModeStatus = cmpManager.getGoogleConsentModeStatus()
262
- val result = Arguments.createMap()
285
+ withCmpManager(promise) { manager ->
286
+ try {
287
+ val consentModeStatus = manager.getGoogleConsentModeStatus()
288
+ val result = Arguments.createMap()
263
289
 
264
- consentModeStatus.forEach { (key, value) ->
265
- result.putString(key, value)
266
- }
290
+ consentModeStatus.forEach { (key, value) ->
291
+ result.putString(key, value)
292
+ }
267
293
 
268
- promise.resolve(result)
269
- } catch (e: Exception) {
270
- promise.reject("ERROR", "Failed to get Google Consent Mode status: ${e.message}", e)
294
+ promise.resolve(result)
295
+ } catch (e: Exception) {
296
+ promise.reject("ERROR", "Failed to get Google Consent Mode status: ${e.message}", e)
297
+ }
271
298
  }
272
299
  }
273
300
 
@@ -277,16 +304,24 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
277
304
  @ReactMethod
278
305
  fun isConsentRequired(promise: Promise) {
279
306
  scope.launch {
280
- try {
281
- cmpManager.isConsentRequired { result ->
282
- if (result.isSuccess) {
283
- promise.resolve(result.getOrNull() ?: false)
284
- } else {
285
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
307
+ withCmpManager(promise) { manager ->
308
+ val activity = currentActivitySafe ?: run {
309
+ promise.reject("NO_ACTIVITY", "Current activity is null. Wait until the app is active before calling isConsentRequired().")
310
+ return@withCmpManager
311
+ }
312
+
313
+ try {
314
+ manager.setActivity(activity)
315
+ manager.isConsentRequired { result ->
316
+ if (result.isSuccess) {
317
+ promise.resolve(result.getOrNull() ?: false)
318
+ } else {
319
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
320
+ }
286
321
  }
322
+ } catch (e: Exception) {
323
+ promise.reject("ERROR", "Failed to check if consent is required: ${e.message}", e)
287
324
  }
288
- } catch (e: Exception) {
289
- promise.reject("ERROR", "Failed to check if consent is required: ${e.message}", e)
290
325
  }
291
326
  }
292
327
  }
@@ -297,18 +332,24 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
297
332
  @ReactMethod
298
333
  fun forceOpen(jumpToSettings: Boolean, promise: Promise) {
299
334
  scope.launch {
300
- try {
301
- currentActivitySafe?.let { cmpManager.setActivity(it) }
335
+ withCmpManager(promise) { manager ->
336
+ val activity = currentActivitySafe ?: run {
337
+ promise.reject("NO_ACTIVITY", "Current activity is null. Wait until the app is active before calling forceOpen().")
338
+ return@withCmpManager
339
+ }
302
340
 
303
- cmpManager.forceOpen(jumpToSettings) { result ->
304
- if (result.isSuccess) {
305
- promise.resolve(true)
306
- } else {
307
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
341
+ try {
342
+ manager.setActivity(activity)
343
+ manager.forceOpen(jumpToSettings) { result ->
344
+ if (result.isSuccess) {
345
+ promise.resolve(true)
346
+ } else {
347
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
348
+ }
308
349
  }
350
+ } catch (e: Exception) {
351
+ promise.reject("ERROR", "Failed to force open consent layer: ${e.message}", e)
309
352
  }
310
- } catch (e: Exception) {
311
- promise.reject("ERROR", "Failed to force open consent layer: ${e.message}", e)
312
353
  }
313
354
  }
314
355
  }
@@ -319,18 +360,24 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
319
360
  @ReactMethod
320
361
  fun checkAndOpen(jumpToSettings: Boolean, promise: Promise) {
321
362
  scope.launch {
322
- try {
323
- currentActivitySafe?.let { cmpManager.setActivity(it) }
363
+ withCmpManager(promise) { manager ->
364
+ val activity = currentActivitySafe ?: run {
365
+ promise.reject("NO_ACTIVITY", "Current activity is null. Wait until the app is active before calling checkAndOpen().")
366
+ return@withCmpManager
367
+ }
324
368
 
325
- cmpManager.checkAndOpen(jumpToSettings) { result ->
326
- if (result.isSuccess) {
327
- promise.resolve(true)
328
- } else {
329
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
369
+ try {
370
+ manager.setActivity(activity)
371
+ manager.checkAndOpen(jumpToSettings) { result ->
372
+ if (result.isSuccess) {
373
+ promise.resolve(true)
374
+ } else {
375
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
376
+ }
330
377
  }
378
+ } catch (e: Exception) {
379
+ promise.reject("ERROR", "Failed to check and open consent: ${e.message}", e)
331
380
  }
332
- } catch (e: Exception) {
333
- promise.reject("ERROR", "Failed to check and open consent: ${e.message}", e)
334
381
  }
335
382
  }
336
383
  }
@@ -341,16 +388,18 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
341
388
  @ReactMethod
342
389
  fun importCMPInfo(cmpString: String, promise: Promise) {
343
390
  scope.launch {
344
- try {
345
- cmpManager.importCMPInfo(cmpString) { result ->
346
- if (result.isSuccess) {
347
- promise.resolve(true)
348
- } else {
349
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
391
+ withCmpManager(promise) { manager ->
392
+ try {
393
+ manager.importCMPInfo(cmpString) { result ->
394
+ if (result.isSuccess) {
395
+ promise.resolve(true)
396
+ } else {
397
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
398
+ }
350
399
  }
400
+ } catch (e: Exception) {
401
+ promise.reject("ERROR", "Failed to import CMP info: ${e.message}", e)
351
402
  }
352
- } catch (e: Exception) {
353
- promise.reject("ERROR", "Failed to import CMP info: ${e.message}", e)
354
403
  }
355
404
  }
356
405
  }
@@ -360,34 +409,44 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
360
409
  */
361
410
  @ReactMethod
362
411
  fun resetConsentManagementData(promise: Promise) {
363
- try {
364
- cmpManager.resetConsentManagementData()
365
- promise.resolve(true)
366
- } catch (e: Exception) {
367
- promise.reject("ERROR", "Failed to reset consent management data: ${e.message}", e)
412
+ withCmpManager(promise) { manager ->
413
+ runOnUiThread {
414
+ try {
415
+ manager.resetConsentManagementData()
416
+ clearWebViewStorage {
417
+ promise.resolve(true)
418
+ }
419
+ } catch (e: Exception) {
420
+ promise.reject("ERROR", "Failed to reset consent management data: ${e.message}", e)
421
+ }
422
+ }
368
423
  }
369
424
  }
370
425
 
371
426
  @ReactMethod
372
427
  fun exportCMPInfo(promise: Promise) {
373
- promise.resolve(cmpManager.exportCMPInfo())
428
+ withCmpManager(promise) { manager ->
429
+ promise.resolve(manager.exportCMPInfo())
430
+ }
374
431
  }
375
432
 
376
433
  @ReactMethod
377
434
  fun acceptVendors(vendors: ReadableArray, promise: Promise) {
378
435
  scope.launch {
379
- try {
380
- Log.d("CmSdkReactNativeV3", "Accepting vendors: $vendors")
381
-
382
- cmpManager.acceptVendors(vendors.toListOfStrings()) { result ->
383
- if (result.isSuccess) {
384
- promise.resolve(null)
385
- } else {
386
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
436
+ withCmpManager(promise) { manager ->
437
+ try {
438
+ Log.d("CmSdkReactNativeV3", "Accepting vendors: $vendors")
439
+
440
+ manager.acceptVendors(vendors.toListOfStrings()) { result ->
441
+ if (result.isSuccess) {
442
+ promise.resolve(true)
443
+ } else {
444
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
445
+ }
387
446
  }
447
+ } catch (e: Exception) {
448
+ promise.reject("ERROR", "Failed to accept vendors: ${e.message}", e)
388
449
  }
389
- } catch (e: Exception) {
390
- promise.reject("ERROR", "Failed to accept vendors: ${e.message}", e)
391
450
  }
392
451
  }
393
452
  }
@@ -395,17 +454,19 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
395
454
  @ReactMethod
396
455
  fun rejectVendors(vendors: ReadableArray, promise: Promise) {
397
456
  scope.launch {
398
- try {
399
- Log.d("CmSdkReactNativeV3", "Rejecting vendors: $vendors")
400
- cmpManager.rejectVendors(vendors.toListOfStrings()) { result ->
401
- if (result.isSuccess) {
402
- promise.resolve(null)
403
- } else {
404
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
457
+ withCmpManager(promise) { manager ->
458
+ try {
459
+ Log.d("CmSdkReactNativeV3", "Rejecting vendors: $vendors")
460
+ manager.rejectVendors(vendors.toListOfStrings()) { result ->
461
+ if (result.isSuccess) {
462
+ promise.resolve(true)
463
+ } else {
464
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
465
+ }
405
466
  }
467
+ } catch (e: Exception) {
468
+ promise.reject("ERROR", "Failed to reject vendors: ${e.message}", e)
406
469
  }
407
- } catch (e: Exception) {
408
- promise.reject("ERROR", "Failed to reject vendors: ${e.message}", e)
409
470
  }
410
471
  }
411
472
  }
@@ -413,18 +474,20 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
413
474
  @ReactMethod
414
475
  fun acceptPurposes(purposes: ReadableArray, updatePurpose: Boolean, promise: Promise) {
415
476
  scope.launch {
416
- try {
417
- Log.d("Cmsdkreactnativev3", "Rejecting purposes: $purposes")
418
-
419
- cmpManager.acceptPurposes(purposes.toListOfStrings(), updatePurpose) { result ->
420
- if (result.isSuccess) {
421
- promise.resolve(null)
422
- } else {
423
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
477
+ withCmpManager(promise) { manager ->
478
+ try {
479
+ Log.d("Cmsdkreactnativev3", "Accepting purposes: $purposes")
480
+
481
+ manager.acceptPurposes(purposes.toListOfStrings(), updatePurpose) { result ->
482
+ if (result.isSuccess) {
483
+ promise.resolve(true)
484
+ } else {
485
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
486
+ }
424
487
  }
488
+ } catch (e: Exception) {
489
+ promise.reject("ERROR", "Failed to accept purposes: ${e.message}", e)
425
490
  }
426
- } catch (e: Exception) {
427
- promise.reject("ERROR", "Failed to accept purposes: ${e.message}", e)
428
491
  }
429
492
  }
430
493
  }
@@ -432,17 +495,19 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
432
495
  @ReactMethod
433
496
  fun rejectPurposes(purposes: ReadableArray, updateVendor: Boolean, promise: Promise) {
434
497
  scope.launch {
435
- try {
436
- Log.d("Cmsdkreactnativev3", "Rejecting purposes: $purposes")
437
- cmpManager.rejectPurposes(purposes.toListOfStrings(), updateVendor) { result ->
438
- if (result.isSuccess) {
439
- promise.resolve(null)
440
- } else {
441
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
498
+ withCmpManager(promise) { manager ->
499
+ try {
500
+ Log.d("Cmsdkreactnativev3", "Rejecting purposes: $purposes")
501
+ manager.rejectPurposes(purposes.toListOfStrings(), updateVendor) { result ->
502
+ if (result.isSuccess) {
503
+ promise.resolve(true)
504
+ } else {
505
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
506
+ }
442
507
  }
508
+ } catch (e: Exception) {
509
+ promise.reject("ERROR", "Failed to reject purposes: ${e.message}", e)
443
510
  }
444
- } catch (e: Exception) {
445
- promise.reject("ERROR", "Failed to reject purposes: ${e.message}", e)
446
511
  }
447
512
  }
448
513
  }
@@ -450,16 +515,18 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
450
515
  @ReactMethod
451
516
  fun rejectAll(promise: Promise) {
452
517
  scope.launch {
453
- try {
454
- cmpManager.rejectAll { result ->
455
- if (result.isSuccess) {
456
- promise.resolve(null)
457
- } else {
458
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
518
+ withCmpManager(promise) { manager ->
519
+ try {
520
+ manager.rejectAll { result ->
521
+ if (result.isSuccess) {
522
+ promise.resolve(true)
523
+ } else {
524
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
525
+ }
459
526
  }
527
+ } catch (e: Exception) {
528
+ promise.reject("ERROR", "Failed to reject all: ${e.message}", e)
460
529
  }
461
- } catch (e: Exception) {
462
- promise.reject("ERROR", "Failed to reject all: ${e.message}", e)
463
530
  }
464
531
  }
465
532
  }
@@ -467,19 +534,98 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
467
534
  @ReactMethod
468
535
  fun acceptAll(promise: Promise) {
469
536
  scope.launch {
470
- try {
471
- cmpManager.acceptAll { result ->
472
- if (result.isSuccess) {
473
- promise.resolve(null)
474
- } else {
475
- promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
537
+ withCmpManager(promise) { manager ->
538
+ try {
539
+ manager.acceptAll { result ->
540
+ if (result.isSuccess) {
541
+ promise.resolve(true)
542
+ } else {
543
+ promise.reject("ERROR", result.exceptionOrNull()?.message ?: "Unknown error")
544
+ }
476
545
  }
546
+ } catch (e: Exception) {
547
+ promise.reject("ERROR", "Failed to accept all: ${e.message}", e)
548
+ }
549
+ }
550
+ }
551
+ }
552
+
553
+ @ReactMethod
554
+ fun setAutomaticConsentUpdatesEnabled(enabled: Boolean, promise: Promise) {
555
+ withCmpManager(promise) { manager ->
556
+ try {
557
+ automaticConsentUpdatesEnabled = enabled
558
+ manager.setAutomaticConsentUpdatesEnabled(enabled)
559
+ promise.resolve(null)
560
+ } catch (e: Exception) {
561
+ promise.reject("ERROR", "Failed to set automatic consent updates: ${e.message}", e)
562
+ }
563
+ }
564
+ }
565
+
566
+ @ReactMethod
567
+ fun updateThirdPartyConsent(promise: Promise) {
568
+ withCmpManager(promise) { manager ->
569
+ try {
570
+ val result = Arguments.createMap()
571
+ manager.updateThirdPartyConsent(reactApplicationContext).forEach { (key, value) ->
572
+ result.putBoolean(key, value)
477
573
  }
574
+ promise.resolve(result)
478
575
  } catch (e: Exception) {
479
- promise.reject("ERROR", "Failed to accept all: ${e.message}", e)
576
+ promise.reject("ERROR", "Failed to update third-party consent: ${e.message}", e)
480
577
  }
481
578
  }
482
579
  }
580
+
581
+ private fun mapBackgroundStyle(config: ReadableMap): ConsentLayerUIConfig.BackgroundStyle {
582
+ val backgroundConfig = if (config.hasKey("backgroundStyle")) config.getMap("backgroundStyle") else null
583
+ val type = backgroundConfig?.getString("type") ?: return ConsentLayerUIConfig.BackgroundStyle.dimmed(android.graphics.Color.BLACK, 0.5f)
584
+
585
+ return when (type) {
586
+ "dimmed" -> ConsentLayerUIConfig.BackgroundStyle.dimmed(
587
+ readOptionalColor(backgroundConfig, "color") ?: android.graphics.Color.BLACK,
588
+ if (backgroundConfig.hasKey("opacity")) backgroundConfig.getDouble("opacity").toFloat() else 0.5f
589
+ )
590
+ "color" -> ConsentLayerUIConfig.BackgroundStyle.solid(
591
+ readOptionalColor(backgroundConfig, "color") ?: android.graphics.Color.BLACK
592
+ )
593
+ "blur" -> ConsentLayerUIConfig.BackgroundStyle.blur(
594
+ readOptionalColor(backgroundConfig, "fallbackColor") ?: android.graphics.Color.BLACK,
595
+ if (backgroundConfig.hasKey("fallbackOpacity")) backgroundConfig.getDouble("fallbackOpacity").toFloat() else 0.5f
596
+ )
597
+ "none" -> ConsentLayerUIConfig.BackgroundStyle.none()
598
+ else -> ConsentLayerUIConfig.BackgroundStyle.dimmed(android.graphics.Color.BLACK, 0.5f)
599
+ }
600
+ }
601
+
602
+ private fun readOptionalColor(config: ReadableMap, key: String): Int? {
603
+ return if (config.hasKey(key) && !config.isNull(key)) config.getDouble(key).toInt() else null
604
+ }
605
+
606
+ private fun mapConsentStatus(status: ConsentStatus): String {
607
+ return when (status) {
608
+ ConsentStatus.CHOICE_DOESNT_EXIST -> "choiceDoesntExist"
609
+ ConsentStatus.GRANTED -> "granted"
610
+ ConsentStatus.DENIED -> "denied"
611
+ }
612
+ }
613
+
614
+ private fun mapUserChoiceStatus(status: UserChoiceStatus): String {
615
+ return when (status) {
616
+ UserChoiceStatus.CHOICE_EXISTS -> "choiceExists"
617
+ UserChoiceStatus.CHOICE_DOESNT_EXIST -> "choiceDoesntExist"
618
+ }
619
+ }
620
+
621
+ private fun withCmpManager(promise: Promise, block: (CMPManager) -> Unit) {
622
+ if (!::cmpManager.isInitialized) {
623
+ promise.reject("NOT_INITIALIZED", "CMPManager is not initialized. Call setUrlConfig() first.")
624
+ return
625
+ }
626
+
627
+ block(cmpManager)
628
+ }
483
629
  private fun ReadableArray.toListOfStrings(): List<String> {
484
630
  val list = mutableListOf<String>()
485
631
  for (i in 0 until this.size()) {
@@ -525,6 +671,20 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
525
671
  }
526
672
  }
527
673
 
674
+ private fun clearWebViewStorage(onComplete: () -> Unit) {
675
+ try {
676
+ val cookieManager = CookieManager.getInstance()
677
+ cookieManager.removeAllCookies {
678
+ cookieManager.flush()
679
+ WebStorage.getInstance().deleteAllData()
680
+ onComplete()
681
+ }
682
+ } catch (e: Exception) {
683
+ Log.w("CmSdkReactNativeV3", "Failed to clear WebView storage: ${e.message}")
684
+ onComplete()
685
+ }
686
+ }
687
+
528
688
  companion object {
529
689
  const val NAME = "CmSdkReactNativeV3"
530
690
  private var globalCMPManager: CMPManager? = null
@@ -533,6 +693,11 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
533
693
 
534
694
  override fun didReceiveConsent(consent: String, jsonObject: Map<String, Any>) {
535
695
  Log.d("CmSdkReactNativeV3", "didReceiveConsent called from native SDK with consent: ${consent.take(50)}...")
696
+ Log.d("CmSdkReactNativeV3", "Consent string length: ${consent.length}")
697
+ Log.d("CmSdkReactNativeV3", "Consent string class: ${consent.javaClass.name}")
698
+ Log.d("CmSdkReactNativeV3", "First char code: ${if (consent.isNotEmpty()) consent[0].code else "empty"}")
699
+ Log.d("CmSdkReactNativeV3", "Last char code: ${if (consent.isNotEmpty()) consent[consent.length - 1].code else "empty"}")
700
+
536
701
  val params = Arguments.createMap().apply {
537
702
  putString("consent", consent)
538
703
  putMap("jsonObject", convertMapToWritableMap(jsonObject))