cm-sdk-react-native-v3 3.6.6 → 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.6.6``
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.7.1"
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,47 @@ 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()
289
+
290
+ consentModeStatus.forEach { (key, value) ->
291
+ result.putString(key, value)
292
+ }
263
293
 
264
- consentModeStatus.forEach { (key, value) ->
265
- result.putString(key, value)
294
+ promise.resolve(result)
295
+ } catch (e: Exception) {
296
+ promise.reject("ERROR", "Failed to get Google Consent Mode status: ${e.message}", e)
266
297
  }
298
+ }
299
+ }
267
300
 
268
- promise.resolve(result)
269
- } catch (e: Exception) {
270
- promise.reject("ERROR", "Failed to get Google Consent Mode status: ${e.message}", e)
301
+ /**
302
+ * Checks if consent is required without opening the consent UI
303
+ */
304
+ @ReactMethod
305
+ fun isConsentRequired(promise: Promise) {
306
+ scope.launch {
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
+ }
321
+ }
322
+ } catch (e: Exception) {
323
+ promise.reject("ERROR", "Failed to check if consent is required: ${e.message}", e)
324
+ }
325
+ }
271
326
  }
272
327
  }
273
328
 
@@ -277,18 +332,24 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
277
332
  @ReactMethod
278
333
  fun forceOpen(jumpToSettings: Boolean, promise: Promise) {
279
334
  scope.launch {
280
- try {
281
- 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
+ }
282
340
 
283
- cmpManager.forceOpen(jumpToSettings) { result ->
284
- if (result.isSuccess) {
285
- promise.resolve(true)
286
- } else {
287
- 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
+ }
288
349
  }
350
+ } catch (e: Exception) {
351
+ promise.reject("ERROR", "Failed to force open consent layer: ${e.message}", e)
289
352
  }
290
- } catch (e: Exception) {
291
- promise.reject("ERROR", "Failed to force open consent layer: ${e.message}", e)
292
353
  }
293
354
  }
294
355
  }
@@ -299,18 +360,24 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
299
360
  @ReactMethod
300
361
  fun checkAndOpen(jumpToSettings: Boolean, promise: Promise) {
301
362
  scope.launch {
302
- try {
303
- 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
+ }
304
368
 
305
- cmpManager.checkAndOpen(jumpToSettings) { result ->
306
- if (result.isSuccess) {
307
- promise.resolve(true)
308
- } else {
309
- 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
+ }
310
377
  }
378
+ } catch (e: Exception) {
379
+ promise.reject("ERROR", "Failed to check and open consent: ${e.message}", e)
311
380
  }
312
- } catch (e: Exception) {
313
- promise.reject("ERROR", "Failed to check and open consent: ${e.message}", e)
314
381
  }
315
382
  }
316
383
  }
@@ -321,16 +388,18 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
321
388
  @ReactMethod
322
389
  fun importCMPInfo(cmpString: String, promise: Promise) {
323
390
  scope.launch {
324
- try {
325
- cmpManager.importCMPInfo(cmpString) { result ->
326
- if (result.isSuccess) {
327
- promise.resolve(true)
328
- } else {
329
- 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
+ }
330
399
  }
400
+ } catch (e: Exception) {
401
+ promise.reject("ERROR", "Failed to import CMP info: ${e.message}", e)
331
402
  }
332
- } catch (e: Exception) {
333
- promise.reject("ERROR", "Failed to import CMP info: ${e.message}", e)
334
403
  }
335
404
  }
336
405
  }
@@ -340,34 +409,44 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
340
409
  */
341
410
  @ReactMethod
342
411
  fun resetConsentManagementData(promise: Promise) {
343
- try {
344
- cmpManager.resetConsentManagementData()
345
- promise.resolve(true)
346
- } catch (e: Exception) {
347
- 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
+ }
348
423
  }
349
424
  }
350
425
 
351
426
  @ReactMethod
352
427
  fun exportCMPInfo(promise: Promise) {
353
- promise.resolve(cmpManager.exportCMPInfo())
428
+ withCmpManager(promise) { manager ->
429
+ promise.resolve(manager.exportCMPInfo())
430
+ }
354
431
  }
355
432
 
356
433
  @ReactMethod
357
434
  fun acceptVendors(vendors: ReadableArray, promise: Promise) {
358
435
  scope.launch {
359
- try {
360
- Log.d("CmSdkReactNativeV3", "Accepting vendors: $vendors")
361
-
362
- cmpManager.acceptVendors(vendors.toListOfStrings()) { result ->
363
- if (result.isSuccess) {
364
- promise.resolve(null)
365
- } else {
366
- 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
+ }
367
446
  }
447
+ } catch (e: Exception) {
448
+ promise.reject("ERROR", "Failed to accept vendors: ${e.message}", e)
368
449
  }
369
- } catch (e: Exception) {
370
- promise.reject("ERROR", "Failed to accept vendors: ${e.message}", e)
371
450
  }
372
451
  }
373
452
  }
@@ -375,17 +454,19 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
375
454
  @ReactMethod
376
455
  fun rejectVendors(vendors: ReadableArray, promise: Promise) {
377
456
  scope.launch {
378
- try {
379
- Log.d("CmSdkReactNativeV3", "Rejecting vendors: $vendors")
380
- cmpManager.rejectVendors(vendors.toListOfStrings()) { result ->
381
- if (result.isSuccess) {
382
- promise.resolve(null)
383
- } else {
384
- 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
+ }
385
466
  }
467
+ } catch (e: Exception) {
468
+ promise.reject("ERROR", "Failed to reject vendors: ${e.message}", e)
386
469
  }
387
- } catch (e: Exception) {
388
- promise.reject("ERROR", "Failed to reject vendors: ${e.message}", e)
389
470
  }
390
471
  }
391
472
  }
@@ -393,18 +474,20 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
393
474
  @ReactMethod
394
475
  fun acceptPurposes(purposes: ReadableArray, updatePurpose: Boolean, promise: Promise) {
395
476
  scope.launch {
396
- try {
397
- Log.d("Cmsdkreactnativev3", "Rejecting purposes: $purposes")
398
-
399
- cmpManager.acceptPurposes(purposes.toListOfStrings(), updatePurpose) { result ->
400
- if (result.isSuccess) {
401
- promise.resolve(null)
402
- } else {
403
- 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
+ }
404
487
  }
488
+ } catch (e: Exception) {
489
+ promise.reject("ERROR", "Failed to accept purposes: ${e.message}", e)
405
490
  }
406
- } catch (e: Exception) {
407
- promise.reject("ERROR", "Failed to accept purposes: ${e.message}", e)
408
491
  }
409
492
  }
410
493
  }
@@ -412,17 +495,19 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
412
495
  @ReactMethod
413
496
  fun rejectPurposes(purposes: ReadableArray, updateVendor: Boolean, promise: Promise) {
414
497
  scope.launch {
415
- try {
416
- Log.d("Cmsdkreactnativev3", "Rejecting purposes: $purposes")
417
- cmpManager.rejectPurposes(purposes.toListOfStrings(), updateVendor) { result ->
418
- if (result.isSuccess) {
419
- promise.resolve(null)
420
- } else {
421
- 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
+ }
422
507
  }
508
+ } catch (e: Exception) {
509
+ promise.reject("ERROR", "Failed to reject purposes: ${e.message}", e)
423
510
  }
424
- } catch (e: Exception) {
425
- promise.reject("ERROR", "Failed to reject purposes: ${e.message}", e)
426
511
  }
427
512
  }
428
513
  }
@@ -430,16 +515,18 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
430
515
  @ReactMethod
431
516
  fun rejectAll(promise: Promise) {
432
517
  scope.launch {
433
- try {
434
- cmpManager.rejectAll { result ->
435
- if (result.isSuccess) {
436
- promise.resolve(null)
437
- } else {
438
- 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
+ }
439
526
  }
527
+ } catch (e: Exception) {
528
+ promise.reject("ERROR", "Failed to reject all: ${e.message}", e)
440
529
  }
441
- } catch (e: Exception) {
442
- promise.reject("ERROR", "Failed to reject all: ${e.message}", e)
443
530
  }
444
531
  }
445
532
  }
@@ -447,19 +534,98 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
447
534
  @ReactMethod
448
535
  fun acceptAll(promise: Promise) {
449
536
  scope.launch {
450
- try {
451
- cmpManager.acceptAll { result ->
452
- if (result.isSuccess) {
453
- promise.resolve(null)
454
- } else {
455
- 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
+ }
456
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)
457
573
  }
574
+ promise.resolve(result)
458
575
  } catch (e: Exception) {
459
- promise.reject("ERROR", "Failed to accept all: ${e.message}", e)
576
+ promise.reject("ERROR", "Failed to update third-party consent: ${e.message}", e)
460
577
  }
461
578
  }
462
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
+ }
463
629
  private fun ReadableArray.toListOfStrings(): List<String> {
464
630
  val list = mutableListOf<String>()
465
631
  for (i in 0 until this.size()) {
@@ -505,6 +671,20 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
505
671
  }
506
672
  }
507
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
+
508
688
  companion object {
509
689
  const val NAME = "CmSdkReactNativeV3"
510
690
  private var globalCMPManager: CMPManager? = null
@@ -513,6 +693,11 @@ class CmSdkReactNativeV3Module(reactContext: ReactApplicationContext) :
513
693
 
514
694
  override fun didReceiveConsent(consent: String, jsonObject: Map<String, Any>) {
515
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
+
516
701
  val params = Arguments.createMap().apply {
517
702
  putString("consent", consent)
518
703
  putMap("jsonObject", convertMapToWritableMap(jsonObject))