From f67b4b2d977000f02360e883136692fbd23976c3 Mon Sep 17 00:00:00 2001 From: Mansi Pandya Date: Mon, 9 Sep 2024 17:30:52 -0400 Subject: [PATCH 1/4] feat: Implement Google EU Consent --- .../kotlin/com/mparticle/kits/AppboyKit.kt | 156 ++++++++++- .../com/mparticle/kits/AppboyKitTest.kt | 255 +++++++++++++++++- 2 files changed, 408 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt index f5a626a..86e41e1 100644 --- a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt +++ b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt @@ -5,6 +5,7 @@ import android.app.Application.ActivityLifecycleCallbacks import android.content.Context import android.content.Intent import android.os.Handler +import android.util.Log import com.braze.Braze import com.braze.BrazeActivityLifecycleCallbackListener import com.braze.BrazeUser @@ -23,11 +24,13 @@ import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Impression import com.mparticle.commerce.Product import com.mparticle.commerce.Promotion +import com.mparticle.consent.ConsentState import com.mparticle.identity.MParticleUser import com.mparticle.internal.Logger import com.mparticle.kits.CommerceEventUtils.OnAttributeExtracted import com.mparticle.kits.KitIntegration.* import org.json.JSONArray +import org.json.JSONException import org.json.JSONObject import java.math.BigDecimal import java.text.SimpleDateFormat @@ -38,7 +41,7 @@ import kotlin.collections.HashMap * mParticle client-side Appboy integration */ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, - KitIntegration.EventListener, PushListener, IdentityListener { + KitIntegration.EventListener, PushListener, IdentityListener ,KitIntegration.UserAttributeListener { var enableTypeDetection = false var bundleCommerceEvents = false @@ -106,7 +109,10 @@ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, if (user != null) { updateUser(user) } - + val userConsentState = currentUser?.consentState + userConsentState?.let { + setConsent(currentUser.consentState) + } return null } @@ -357,7 +363,148 @@ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, }) } + override fun onIncrementUserAttribute( + key: String?, + incrementedBy: Number?, + value: String?, + user: FilteredMParticleUser? + ) { + } + + override fun onRemoveUserAttribute(key: String?, user: FilteredMParticleUser?) { + } + + override fun onSetUserAttribute(key: String?, value: Any?, user: FilteredMParticleUser?) { + } + + override fun onSetUserTag(key: String?, user: FilteredMParticleUser?) { + } + + override fun onSetUserAttributeList( + attributeKey: String?, + attributeValueList: MutableList?, + user: FilteredMParticleUser? + ) { + } + + override fun onSetAllUserAttributes( + userAttributes: MutableMap?, + userAttributeLists: MutableMap>?, + user: FilteredMParticleUser? + ) { + } + override fun supportsAttributeLists(): Boolean = true + override fun onConsentStateUpdated( + oldState: ConsentState, + newState: ConsentState, + user: FilteredMParticleUser + ) { + setConsent(newState) + + + } + + private fun setConsent(consentState: ConsentState) { + val clientConsentSettings = parseToNestedMap(consentState.toString()) + + parseConsentMapping(settings[consentMappingSDK]).iterator().forEach { currentConsent -> + val isConsentAvailable = + searchKeyInNestedMap(clientConsentSettings, key = currentConsent.key) + + if (isConsentAvailable != null) { + val isConsentGranted: Boolean = + JSONObject(isConsentAvailable.toString()).opt("consented") as Boolean + + when (currentConsent.value) { + "google_ad_user_data" -> setConsentValueToBraze( + KEY_GOOGLE_AD_USER_DATA, isConsentGranted + ) + + "google_ad_personalization" -> setConsentValueToBraze( + KEY_GOOGLE_AD_PERSONALIZATION, isConsentGranted + ) + + } + } + } + } + + private fun setConsentValueToBraze(key: String, value: Boolean) { + Braze.getInstance(context).getCurrentUser(object : IValueCallback { + override fun onSuccess(brazeUser: BrazeUser) { + brazeUser.setCustomUserAttribute(key, value) + } + + override fun onError() { + super.onError() + } + }) + } + + private fun parseConsentMapping(json: String?): Map { + if (json.isNullOrEmpty()) { + return emptyMap() + } + val jsonWithFormat = json.replace("\\", "") + + return try { + JSONArray(jsonWithFormat) + .let { jsonArray -> + (0 until jsonArray.length()) + .associate { + val jsonObject = jsonArray.getJSONObject(it) + val map = jsonObject.getString("map") + val value = jsonObject.getString("value") + map to value + } + } + } catch (jse: JSONException) { + Logger.warning(jse, "The Google Firebase kit threw an exception while searching for the configured consent purpose mapping in the current user's consent status.") + emptyMap() + } + } + + private fun parseToNestedMap(jsonString: String): Map { + val topLevelMap = mutableMapOf() + try { + val jsonObject = JSONObject(jsonString) + + for (key in jsonObject.keys()) { + val value = jsonObject.get(key) + if (value is JSONObject) { + topLevelMap[key] = parseToNestedMap(value.toString()) + } else { + topLevelMap[key] = value + } + } + } catch (e: Exception) { + Logger.error(e, "The Google Firebase kit was unable to parse the user's ConsentState, consent may not be set correctly on the Google Analytics SDK") + } + return topLevelMap + } + + private fun searchKeyInNestedMap(map: Map<*, *>, key: Any): Any? { + if (map.isNullOrEmpty()) { + return null + } + try { + for ((mapKey, mapValue) in map) { + if (mapKey.toString().equals(key.toString(), ignoreCase = true)) { + return mapValue + } + if (mapValue is Map<*, *>) { + val foundValue = searchKeyInNestedMap(mapValue, key) + if (foundValue != null) { + return foundValue + } + } + } + } catch (e: Exception) { + Logger.error(e, "The Google Firebase kit threw an exception while searching for the configured consent purpose mapping in the current user's consent status.") + } + return null + } protected open fun queueDataFlush() { dataFlushRunnable?.let { dataFlushHandler.removeCallbacks(it) } @@ -944,6 +1091,11 @@ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, private const val UNSUBSCRIBED = "unsubscribed" private const val SUBSCRIBED = "subscribed" + //Constants for Read Consent + private const val consentMappingSDK = "consentMappingSDK" + private const val KEY_GOOGLE_AD_USER_DATA = "\$google_ad_user_data" + private const val KEY_GOOGLE_AD_PERSONALIZATION = "\$google_ad_personalization" + const val CUSTOM_ATTRIBUTES_KEY = "Attributes" const val PRODUCT_KEY = "products" const val PROMOTION_KEY = "promotions" diff --git a/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt b/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt index fb3e524..4357549 100644 --- a/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt +++ b/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt @@ -1,5 +1,8 @@ package com.mparticle.kits +import android.app.Activity +import android.content.Context +import android.net.Uri import android.util.SparseBooleanArray import com.braze.Braze import com.braze.models.outgoing.BrazeProperties @@ -7,17 +10,23 @@ import com.mparticle.MPEvent import com.mparticle.MParticle import com.mparticle.MParticle.IdentityType import com.mparticle.MParticleOptions +import com.mparticle.MParticleOptions.DataplanOptions import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Impression import com.mparticle.commerce.Product import com.mparticle.commerce.Promotion import com.mparticle.commerce.TransactionAttributes +import com.mparticle.consent.ConsentState +import com.mparticle.consent.GDPRConsent import com.mparticle.identity.IdentityApi import com.mparticle.identity.MParticleUser +import com.mparticle.internal.CoreCallbacks +import com.mparticle.internal.CoreCallbacks.KitListener import com.mparticle.kits.mocks.MockAppboyKit import com.mparticle.kits.mocks.MockContextApplication import com.mparticle.kits.mocks.MockKitConfiguration import com.mparticle.kits.mocks.MockUser +import junit.framework.TestCase import org.json.JSONArray import org.json.JSONObject import org.junit.Assert @@ -25,7 +34,10 @@ import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations +import java.lang.ref.WeakReference +import java.lang.reflect.Method import java.math.BigDecimal import java.util.Calendar import java.util.Locale @@ -39,6 +51,12 @@ class AppboyKitTests { @Mock private val mTypeFilters: SparseBooleanArray? = null + @Mock + lateinit var filteredMParticleUser: FilteredMParticleUser + + @Mock + lateinit var user: MParticleUser + private val kit: AppboyKit get() = AppboyKit() @@ -47,6 +65,7 @@ class AppboyKitTests { MockitoAnnotations.initMocks(this) Braze.clearPurchases() Braze.clearEvents() + Braze.currentUser.customUserAttributes.clear() MParticle.setInstance(Mockito.mock(MParticle::class.java)) Mockito.`when`(MParticle.getInstance()!!.Identity()).thenReturn( Mockito.mock( @@ -54,7 +73,7 @@ class AppboyKitTests { ) ) braze = Braze - braze.currentUser.getCustomAttribute().clear() + } @Test @@ -1043,4 +1062,238 @@ class AppboyKitTests { } Assert.assertEquals("testEvent", outputKey) } + + @Test + fun testParseToNestedMap_When_JSON_Is_INVALID() { + val kit = MockAppboyKit() + var jsonInput = + "{'GDPR':{'marketing':'{:false,'timestamp':1711038269644:'Test consent','location':'17 Cherry Tree Lane','hardware_id':'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702'}','performance':'{'consented':true,'timestamp':1711038269644,'document':'parental_consent_agreement_v2','location':'17 Cherry Tree Lan 3','hardware_id':'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702'}'},'CCPA':'{'consented':true,'timestamp':1711038269644,'document':'ccpa_consent_agreement_v3','location':'17 Cherry Tree Lane','hardware_id':'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702'}'}" + + val method: Method = AppboyKit::class.java.getDeclaredMethod( + "parseToNestedMap", + String::class.java + ) + method.isAccessible = true + val result = method.invoke(kit, jsonInput) + Assert.assertEquals(mutableMapOf(), result) + } + + @Test + fun testParseToNestedMap_When_JSON_Is_Empty() { + val kit = MockAppboyKit() + var jsonInput = "" + + val method: Method = AppboyKit::class.java.getDeclaredMethod( + "parseToNestedMap", + String::class.java + ) + method.isAccessible = true + val result = method.invoke(kit, jsonInput) + Assert.assertEquals(mutableMapOf(), result) + } + + @Test + fun testSearchKeyInNestedMap_When_Input_Key_Is_Empty_String() { + val kit = MockAppboyKit() + val map = mapOf( + "GDPR" to true, + "marketing" to mapOf( + "consented" to false, + "document" to mapOf( + "timestamp" to 1711038269644 + ) + ) + ) + val method: Method = AppboyKit::class.java.getDeclaredMethod( + "searchKeyInNestedMap", Map::class.java, + Any::class.java + ) + method.isAccessible = true + val result = method.invoke(kit, map, "") + Assert.assertEquals(null, result) + } + + @Test + fun testSearchKeyInNestedMap_When_Input_Is_Empty_Map() { + val kit = MockAppboyKit() + val emptyMap: Map = emptyMap() + val method: Method = AppboyKit::class.java.getDeclaredMethod( + "searchKeyInNestedMap", Map::class.java, + Any::class.java + ) + method.isAccessible = true + val result = method.invoke(kit, emptyMap, "1") + Assert.assertEquals(null, result) + } + + @Test + fun testParseConsentMapping_When_Input_Is_Empty_Json() { + val kit = MockAppboyKit() + val emptyJson = "" + val method: Method = AppboyKit::class.java.getDeclaredMethod( + "parseConsentMapping", + String::class.java + ) + method.isAccessible = true + val result = method.invoke(kit, emptyJson) + Assert.assertEquals(emptyMap(), result) + } + + @Test + fun testParseConsentMapping_When_Input_Is_Invalid_Json() { + val kit = MockAppboyKit() + var jsonInput = + "{'GDPR':{'marketing':'{:false,'timestamp':1711038269644:'Test consent','location':'17 Cherry Tree Lane','hardware_id':'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702'}','performance':'{'consented':true,'timestamp':1711038269644,'document':'parental_consent_agreement_v2','location':'17 Cherry Tree Lan 3','hardware_id':'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702'}'},'CCPA':'{'consented':true,'timestamp':1711038269644,'document':'ccpa_consent_agreement_v3','location':'17 Cherry Tree Lane','hardware_id':'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702'}'}" + val method: Method = AppboyKit::class.java.getDeclaredMethod( + "parseConsentMapping", + String::class.java + ) + method.isAccessible = true + val result = method.invoke(kit, jsonInput) + Assert.assertEquals(emptyMap(), result) + } + + @Test + fun testParseConsentMapping_When_Input_Is_NULL() { + val kit = MockAppboyKit() + val method: Method = AppboyKit::class.java.getDeclaredMethod( + "parseConsentMapping", + String::class.java + ) + method.isAccessible = true + val result = method.invoke(kit, null) + Assert.assertEquals(emptyMap(), result) + } + + @Test + fun onConsentStateUpdatedTest() { + val kit = MockAppboyKit() + val currentUser = braze.currentUser + kit.configuration = MockKitConfiguration() + val map = java.util.HashMap() + + map["consentMappingSDK"] = + " [{\\\"jsmap\\\":null,\\\"map\\\":\\\"Performance\\\",\\\"maptype\\\":\\\"ConsentPurposes\\\",\\\"value\\\":\\\"google_ad_user_data\\\"},{\\\"jsmap\\\":null,\\\"map\\\":\\\"Marketing\\\",\\\"maptype\\\":\\\"ConsentPurposes\\\",\\\"value\\\":\\\"google_ad_personalization\\\"}]" + + + var kitConfiguration = + MockKitConfiguration.createKitConfiguration(JSONObject().put("as", map.toMutableMap())) + kit.configuration = kitConfiguration + + val marketingConsent = GDPRConsent.builder(false) + .document("Test consent") + .location("17 Cherry Tree Lane") + .hardwareId("IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702") + .build() + val state = ConsentState.builder() + .addGDPRConsentState("Marketing", marketingConsent) + .build() + filteredMParticleUser = FilteredMParticleUser.getInstance(user, kit) + + kit.onConsentStateUpdated(state, state, filteredMParticleUser) + TestCase.assertEquals(false, currentUser.getCustomUserAttribute()["\$google_ad_personalization"]) + + } + + @Test + fun onConsentStateUpdatedTest_When_Both_The_consents_Are_True() { + val kit = MockAppboyKit() + val currentUser = braze.currentUser + kit.configuration = MockKitConfiguration() + val map = java.util.HashMap() + + map["consentMappingSDK"] = + " [{\\\"jsmap\\\":null,\\\"map\\\":\\\"Performance\\\",\\\"maptype\\\":\\\"ConsentPurposes\\\",\\\"value\\\":\\\"google_ad_user_data\\\"},{\\\"jsmap\\\":null,\\\"map\\\":\\\"Marketing\\\",\\\"maptype\\\":\\\"ConsentPurposes\\\",\\\"value\\\":\\\"google_ad_personalization\\\"}]" + + + var kitConfiguration = + MockKitConfiguration.createKitConfiguration(JSONObject().put("as", map.toMutableMap())) + kit.configuration = kitConfiguration + + val marketingConsent = GDPRConsent.builder(true) + .document("Test consent") + .location("17 Cherry Tree Lane") + .hardwareId("IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702") + .build() + + val performanceConsent = GDPRConsent.builder(true) + .document("parental_consent_agreement_v2") + .location("17 Cherry Tree Lan 3") + .hardwareId("IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702") + .build() + + val state = ConsentState.builder() + .addGDPRConsentState("Marketing", marketingConsent) + .addGDPRConsentState("Performance", performanceConsent) + .build() + filteredMParticleUser = FilteredMParticleUser.getInstance(user, kit) + + kit.onConsentStateUpdated(state, state, filteredMParticleUser) + TestCase.assertEquals(true, currentUser.getCustomUserAttribute()["\$google_ad_user_data"]) + TestCase.assertEquals(true, currentUser.getCustomUserAttribute()["\$google_ad_personalization"]) + } + + @Test + fun onConsentStateUpdatedTest_When_No_DATA_From_Server() { + val kit = MockAppboyKit() + val currentUser = braze.currentUser + kit.configuration = MockKitConfiguration() + val marketingConsent = GDPRConsent.builder(true) + .document("Test consent") + .location("17 Cherry Tree Lane") + .hardwareId("IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702") + .build() + + val performanceConsent = GDPRConsent.builder(true) + .document("parental_consent_agreement_v2") + .location("17 Cherry Tree Lan 3") + .hardwareId("IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702") + .build() + + val state = ConsentState.builder() + .addGDPRConsentState("Marketing", marketingConsent) + .addGDPRConsentState("Performance", performanceConsent) + .build() + filteredMParticleUser = FilteredMParticleUser.getInstance(user, kit) + kit.onConsentStateUpdated(state, state, filteredMParticleUser) + TestCase.assertEquals(0, currentUser.getCustomUserAttribute().size) + } + + @Test + fun testOnConsentStateUpdatedTest_No_consentMappingSDK() { + val kit = MockAppboyKit() + val currentUser = braze.currentUser + kit.configuration = MockKitConfiguration() + val map = java.util.HashMap() + map["includeEnrichedUserAttributes"] = "True" + map["userIdentificationType"] = "MPID" + map["ABKDisableAutomaticLocationCollectionKey"] = "False" + map["defaultAdPersonalizationConsentSDK"] = "Denied" + + kit.configuration = + KitConfiguration.createKitConfiguration(JSONObject().put("as", map.toMutableMap())) + + val marketingConsent = GDPRConsent.builder(true) + .document("Test consent") + .location("17 Cherry Tree Lane") + .hardwareId("IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702") + .build() + + val performanceConsent = GDPRConsent.builder(true) + .document("parental_consent_agreement_v2") + .location("17 Cherry Tree Lan 3") + .hardwareId("IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702") + .build() + + val state = ConsentState.builder() + .addGDPRConsentState("Marketing", marketingConsent) + .addGDPRConsentState("Performance", performanceConsent) + .build() + filteredMParticleUser = FilteredMParticleUser.getInstance(user, kit) + + kit.onConsentStateUpdated(state, state, filteredMParticleUser) + + TestCase.assertEquals(0, currentUser.getCustomUserAttribute().size) + + } } From f00a61e3557bfb6ddc0704098d39d37bb76bdf8c Mon Sep 17 00:00:00 2001 From: Mansi Pandya Date: Mon, 9 Sep 2024 17:44:57 -0400 Subject: [PATCH 2/4] update error message --- src/main/kotlin/com/mparticle/kits/AppboyKit.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt index 86e41e1..683f8ce 100644 --- a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt +++ b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt @@ -460,7 +460,7 @@ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, } } } catch (jse: JSONException) { - Logger.warning(jse, "The Google Firebase kit threw an exception while searching for the configured consent purpose mapping in the current user's consent status.") + Logger.warning(jse, "The Braze kit threw an exception while searching for the configured consent purpose mapping in the current user's consent status.") emptyMap() } } @@ -479,7 +479,7 @@ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, } } } catch (e: Exception) { - Logger.error(e, "The Google Firebase kit was unable to parse the user's ConsentState, consent may not be set correctly on the Google Analytics SDK") + Logger.error(e, "The Braze kit was unable to parse the user's ConsentState, consent may not be set correctly on the Braze SDK") } return topLevelMap } @@ -501,7 +501,7 @@ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, } } } catch (e: Exception) { - Logger.error(e, "The Google Firebase kit threw an exception while searching for the configured consent purpose mapping in the current user's consent status.") + Logger.error(e, "The Braze kit threw an exception while searching for the configured consent purpose mapping in the current user's consent status.") } return null } From 61b3b6007c804e7c13d1720cf5c69c3027dcabc1 Mon Sep 17 00:00:00 2001 From: Mansi Pandya Date: Tue, 10 Sep 2024 11:38:17 -0400 Subject: [PATCH 3/4] Removed Extra space --- src/main/kotlin/com/mparticle/kits/AppboyKit.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt index 683f8ce..4d4ac22 100644 --- a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt +++ b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt @@ -401,8 +401,6 @@ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, user: FilteredMParticleUser ) { setConsent(newState) - - } private fun setConsent(consentState: ConsentState) { From c201a65f22b49c94045adda5c52b5410e0520994 Mon Sep 17 00:00:00 2001 From: Mansi Pandya Date: Tue, 10 Sep 2024 14:07:23 -0400 Subject: [PATCH 4/4] Address review comment --- .../kotlin/com/mparticle/kits/AppboyKit.kt | 2 +- .../com/mparticle/kits/AppboyKitTest.kt | 20 +++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt index 4d4ac22..5816b7b 100644 --- a/src/main/kotlin/com/mparticle/kits/AppboyKit.kt +++ b/src/main/kotlin/com/mparticle/kits/AppboyKit.kt @@ -41,7 +41,7 @@ import kotlin.collections.HashMap * mParticle client-side Appboy integration */ open class AppboyKit : KitIntegration(), AttributeListener, CommerceListener, - KitIntegration.EventListener, PushListener, IdentityListener ,KitIntegration.UserAttributeListener { + KitIntegration.EventListener, PushListener, IdentityListener, KitIntegration.UserAttributeListener { var enableTypeDetection = false var bundleCommerceEvents = false diff --git a/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt b/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt index 4357549..75a7cd9 100644 --- a/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt +++ b/src/test/kotlin/com/mparticle/kits/AppboyKitTest.kt @@ -1,8 +1,5 @@ package com.mparticle.kits -import android.app.Activity -import android.content.Context -import android.net.Uri import android.util.SparseBooleanArray import com.braze.Braze import com.braze.models.outgoing.BrazeProperties @@ -10,7 +7,6 @@ import com.mparticle.MPEvent import com.mparticle.MParticle import com.mparticle.MParticle.IdentityType import com.mparticle.MParticleOptions -import com.mparticle.MParticleOptions.DataplanOptions import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Impression import com.mparticle.commerce.Product @@ -20,8 +16,6 @@ import com.mparticle.consent.ConsentState import com.mparticle.consent.GDPRConsent import com.mparticle.identity.IdentityApi import com.mparticle.identity.MParticleUser -import com.mparticle.internal.CoreCallbacks -import com.mparticle.internal.CoreCallbacks.KitListener import com.mparticle.kits.mocks.MockAppboyKit import com.mparticle.kits.mocks.MockContextApplication import com.mparticle.kits.mocks.MockKitConfiguration @@ -34,9 +28,7 @@ import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations -import java.lang.ref.WeakReference import java.lang.reflect.Method import java.math.BigDecimal import java.util.Calendar @@ -1096,11 +1088,13 @@ class AppboyKitTests { fun testSearchKeyInNestedMap_When_Input_Key_Is_Empty_String() { val kit = MockAppboyKit() val map = mapOf( - "GDPR" to true, - "marketing" to mapOf( - "consented" to false, - "document" to mapOf( - "timestamp" to 1711038269644 + "FeatureEnabled" to true, + "settings" to mapOf( + "darkMode" to false, + "notifications" to mapOf( + "email" to false, + "push" to true, + "lastUpdated" to 1633046400000L ) ) )