Skip to content

Commit 72235e0

Browse files
authored
feat: expose device-based consent APIs in React Native bridge (#351)
1 parent ebc47e9 commit 72235e0

6 files changed

Lines changed: 325 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- Expose device-level consent APIs: `setDeviceConsentState`, `clearDeviceConsentState`, and `getDeviceConsentState`, bridging to native `MParticle.deviceConsentState` (iOS) and `MParticle.setDeviceConsentState()` (Android). Requires mParticle Apple SDK 9.2+ with device consent; Android resolves `android-core` `[5.79.2, 6.0)` and picks up device consent APIs once published.
13+
1214
- Expo config plugin: optional `pinningDisabled` for `MPNetworkOptions` / `NetworkOptions` at SDK startup
1315

1416
### Fixed

android/src/main/java/com/mparticle/react/MParticleModule.kt

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,31 @@ class MParticleModule(
534534
}
535535
}
536536

537+
@ReactMethod
538+
override fun setDeviceConsentState(consentState: ReadableMap?) {
539+
val instance = MParticle.getInstance() ?: return
540+
if (consentState == null) {
541+
return
542+
}
543+
val state = convertToConsentState(consentState)
544+
instance.setDeviceConsentState(if (isEmptyConsentState(state)) null else state)
545+
}
546+
547+
@ReactMethod
548+
override fun clearDeviceConsentState() {
549+
MParticle.getInstance()?.setDeviceConsentState(null)
550+
}
551+
552+
@ReactMethod
553+
override fun getDeviceConsentState(callback: Callback) {
554+
val instance = MParticle.getInstance()
555+
if (instance == null) {
556+
callback.invoke(null)
557+
return
558+
}
559+
callback.invoke(consentStateToMap(instance.getDeviceConsentState()))
560+
}
561+
537562
protected fun getWritableMap(): WritableMap = WritableNativeMap()
538563

539564
private fun convertIdentityAPIRequest(map: ReadableMap?): IdentityApiRequest {
@@ -928,17 +953,30 @@ class MParticleModule(
928953
map.getString("location")?.let { builder.location(it) }
929954
}
930955
if (map.hasKey("timestamp")) {
931-
try {
932-
val timestampString = map.getString("timestamp")
933-
val timestamp = timestampString?.toLong()
934-
timestamp?.let { builder.timestamp(it) }
935-
} catch (ex: Exception) {
936-
Logger.warning("failed to convert \"timestamp\" value to Long")
937-
}
956+
readConsentTimestampMillis(map, "timestamp")?.let { builder.timestamp(it) }
938957
}
939958
return builder.build()
940959
}
941960

961+
private fun readConsentTimestampMillis(
962+
map: ReadableMap,
963+
key: String,
964+
): Long? {
965+
if (!map.hasKey(key)) {
966+
return null
967+
}
968+
return try {
969+
when (map.getType(key)) {
970+
ReadableType.Number -> map.getDouble(key).toLong()
971+
ReadableType.String -> map.getString(key)?.toLongOrNull()
972+
else -> null
973+
}
974+
} catch (ex: Exception) {
975+
Logger.warning("failed to convert \"$key\" timestamp value to Long")
976+
null
977+
}
978+
}
979+
942980
private fun convertToCCPAConsent(map: ReadableMap): CCPAConsent? {
943981
val consented =
944982
try {
@@ -963,14 +1001,67 @@ class MParticleModule(
9631001
map.getString("location")?.let { builder.location(it) }
9641002
}
9651003
if (map.hasKey("timestamp")) {
966-
try {
967-
val timestampString = map.getString("timestamp")
968-
val timestamp = timestampString?.toLong()
969-
timestamp?.let { builder.timestamp(it) }
970-
} catch (ex: Exception) {
971-
Logger.warning("failed to convert \"timestamp\" value to Long")
1004+
readConsentTimestampMillis(map, "timestamp")?.let { builder.timestamp(it) }
1005+
}
1006+
return builder.build()
1007+
}
1008+
1009+
private fun convertToConsentState(map: ReadableMap): ConsentState {
1010+
val builder = ConsentState.builder()
1011+
if (map.hasKey("gdpr")) {
1012+
map.getMap("gdpr")?.let { gdprMap ->
1013+
val iterator = gdprMap.keySetIterator()
1014+
while (iterator.hasNextKey()) {
1015+
val purpose = iterator.nextKey()
1016+
val consentMap = gdprMap.getMap(purpose) ?: continue
1017+
convertToGDPRConsent(consentMap)?.let { builder.addGDPRConsentState(purpose, it) }
1018+
}
1019+
}
1020+
}
1021+
if (map.hasKey("ccpa")) {
1022+
map.getMap("ccpa")?.let { ccpaMap ->
1023+
convertToCCPAConsent(ccpaMap)?.let { builder.setCCPAConsentState(it) }
9721024
}
9731025
}
9741026
return builder.build()
9751027
}
1028+
1029+
private fun isEmptyConsentState(state: ConsentState) = state.gdprConsentState.isEmpty() && state.ccpaConsentState == null
1030+
1031+
private fun consentStateToMap(state: ConsentState?): WritableMap? {
1032+
if (state == null) {
1033+
return null
1034+
}
1035+
val result = Arguments.createMap()
1036+
val gdprConsentState = state.gdprConsentState
1037+
if (gdprConsentState.isNotEmpty()) {
1038+
val gdprMap = Arguments.createMap()
1039+
for ((purpose, consent) in gdprConsentState) {
1040+
gdprMap.putMap(purpose, gdprConsentToMap(consent))
1041+
}
1042+
result.putMap("gdpr", gdprMap)
1043+
}
1044+
state.ccpaConsentState?.let { result.putMap("ccpa", ccpaConsentToMap(it)) }
1045+
return if (result.toHashMap().isEmpty()) null else result
1046+
}
1047+
1048+
private fun gdprConsentToMap(consent: GDPRConsent): WritableMap {
1049+
val map = Arguments.createMap()
1050+
map.putBoolean("consented", consent.isConsented)
1051+
consent.document?.let { map.putString("document", it) }
1052+
consent.location?.let { map.putString("location", it) }
1053+
consent.hardwareId?.let { map.putString("hardwareId", it) }
1054+
consent.timestamp?.let { map.putDouble("timestamp", it.toDouble()) }
1055+
return map
1056+
}
1057+
1058+
private fun ccpaConsentToMap(consent: CCPAConsent): WritableMap {
1059+
val map = Arguments.createMap()
1060+
map.putBoolean("consented", consent.isConsented)
1061+
consent.document?.let { map.putString("document", it) }
1062+
consent.location?.let { map.putString("location", it) }
1063+
consent.hardwareId?.let { map.putString("hardwareId", it) }
1064+
consent.timestamp?.let { map.putDouble("timestamp", it.toDouble()) }
1065+
return map
1066+
}
9761067
}

android/src/oldarch/java/com/mparticle/react/NativeMParticleSpec.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ abstract class NativeMParticleSpec(
5757

5858
abstract fun removeCCPAConsentState()
5959

60+
abstract fun setDeviceConsentState(consentState: ReadableMap?)
61+
62+
abstract fun clearDeviceConsentState()
63+
64+
abstract fun getDeviceConsentState(callback: Callback)
65+
6066
abstract fun isKitActive(
6167
kitId: Double,
6268
callback: Callback,

0 commit comments

Comments
 (0)