Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 99 additions & 6 deletions src/main/kotlin/com/mparticle/kits/AppsFlyerKit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.mparticle.MParticle
import com.mparticle.commerce.CommerceEvent
import com.mparticle.commerce.Product
import com.mparticle.consent.ConsentState
import com.mparticle.identity.MParticleUser
import com.mparticle.internal.Logger
import com.mparticle.internal.MPUtility
import org.json.JSONArray
Expand All @@ -47,7 +48,8 @@ class AppsFlyerKit :
KitIntegration.CommerceListener,
AppsFlyerConversionListener,
KitIntegration.ActivityListener,
KitIntegration.UserAttributeListener {
KitIntegration.UserAttributeListener,
KitIntegration.IdentityListener {
override fun getInstance(): AppsFlyerLib = AppsFlyerLib.getInstance()

override fun getName() = NAME
Expand All @@ -74,6 +76,8 @@ class AppsFlyerKit :
setIntegrationAttributes(integrationAttributes)
AppsFlyerLib.getInstance().subscribeForDeepLink(deepLinkListener())

updateCustomerUserIDIfNeededForUser(currentUser)

val messages: MutableList<ReportingMessage> = ArrayList()
messages.add(
ReportingMessage(
Expand Down Expand Up @@ -107,6 +111,7 @@ class AppsFlyerKit :
): List<ReportingMessage> = emptyList()

override fun logEvent(event: CommerceEvent): List<ReportingMessage> {
val event = commerceEventWithAppsFlyerCustomerUserId(event)
val messages: MutableList<ReportingMessage> = LinkedList()
val eventValues: MutableMap<String, Any?> = HashMap()
val productList = event.products
Expand Down Expand Up @@ -215,9 +220,13 @@ class AppsFlyerKit :
event.productAction == Product.PURCHASE

override fun logEvent(event: MPEvent): List<ReportingMessage> {
var hashMap: HashMap<String?, Any?>? = hashMapOf()
if (event.customAttributes?.isNotEmpty() == true) {
hashMap = event.customAttributes?.let { HashMap(it) }
val hashMap: HashMap<String?, Any?> =
event.customAttributes
?.takeIf { it.isNotEmpty() }
?.let { HashMap(it) }
?: hashMapOf()
customerIdForAppsFlyer(currentUser)?.takeIf { it.isNotEmpty() }?.let { cid ->
hashMap[AF_CUSTOMER_USER_ID] = cid
}
instance.logEvent(context, event.eventName, hashMap)
val messages: MutableList<ReportingMessage> = LinkedList()
Expand Down Expand Up @@ -307,7 +316,11 @@ class AppsFlyerKit :
override fun removeUserIdentity(identityType: MParticle.IdentityType) {
with(instance) {
if (MParticle.IdentityType.CustomerId == identityType) {
setCustomerUserId("")
if (isUserIdentificationMPID() || isUserIdentificationCustomerId()) {
updateCustomerUserIDIfNeededForUser(currentUser)
} else {
setCustomerUserId("")
}
} else if (MParticle.IdentityType.Email == identityType) {
setUserEmails(AppsFlyerProperties.EmailsCryptType.NONE, "")
}
Expand All @@ -320,7 +333,9 @@ class AppsFlyerKit :
) {
with(instance) {
if (MParticle.IdentityType.CustomerId == identityType) {
setCustomerUserId(identity)
if (!isUserIdentificationMPID()) {
setCustomerUserId(identity)
}
} else if (MParticle.IdentityType.Email == identityType) {
setUserEmails(AppsFlyerProperties.EmailsCryptType.NONE, identity)
}
Expand All @@ -329,6 +344,71 @@ class AppsFlyerKit :

override fun logout(): List<ReportingMessage> = emptyList()

override fun onIdentifyCompleted(
mParticleUser: MParticleUser,
identityApiRequest: FilteredIdentityApiRequest,
) {
updateCustomerUserIDIfNeededForUser(mParticleUser)
}

override fun onLoginCompleted(
mParticleUser: MParticleUser,
identityApiRequest: FilteredIdentityApiRequest,
) {
updateCustomerUserIDIfNeededForUser(mParticleUser)
}

override fun onLogoutCompleted(
mParticleUser: MParticleUser,
identityApiRequest: FilteredIdentityApiRequest,
) {
updateCustomerUserIDIfNeededForUser(mParticleUser)
}

override fun onModifyCompleted(
mParticleUser: MParticleUser,
identityApiRequest: FilteredIdentityApiRequest,
) {
updateCustomerUserIDIfNeededForUser(mParticleUser)
}

override fun onUserIdentified(mParticleUser: MParticleUser) {
updateCustomerUserIDIfNeededForUser(mParticleUser)
}

private fun isUserIdentificationMPID(): Boolean = USER_IDENTIFICATION_MPID == settings[USER_IDENTIFICATION_TYPE]

private fun isUserIdentificationCustomerId(): Boolean = USER_IDENTIFICATION_CUSTOMER_ID == settings[USER_IDENTIFICATION_TYPE]

private fun customerIdForAppsFlyer(user: MParticleUser?): String? {
if (user == null) {
return null
}
return when {
isUserIdentificationMPID() -> user.id.toString()
isUserIdentificationCustomerId() ->
user.getUserIdentities()[MParticle.IdentityType.CustomerId]
else -> user.id.toString()
}
}

private fun updateCustomerUserIDIfNeededForUser(user: MParticleUser?) {
if (!isUserIdentificationMPID() && !isUserIdentificationCustomerId()) {
return
}
instance.setCustomerUserId(customerIdForAppsFlyer(user))
}

private fun commerceEventWithAppsFlyerCustomerUserId(event: CommerceEvent): CommerceEvent {
val cid = customerIdForAppsFlyer(currentUser)?.takeIf { it.isNotEmpty() } ?: return event
val newAttrs = HashMap<String, String>()
event.customAttributes?.forEach { (key, value) ->
newAttrs[key] = value?.toString() ?: ""
}
newAttrs[AF_CUSTOMER_USER_ID] = cid
return CommerceEvent.Builder(event).customAttributes(newAttrs).build()
}

private fun parseToNestedMap(jsonString: String): Map<String, Any> {
val topLevelMap = mutableMapOf<String, Any>()
try {
Expand Down Expand Up @@ -608,6 +688,19 @@ class AppsFlyerKit :
}

const val MANUAL_START = "manualStart"

/**
* Kit setting: use [USER_IDENTIFICATION_MPID], [USER_IDENTIFICATION_CUSTOMER_ID], or omit for legacy (MPID).
* When set to [USER_IDENTIFICATION_MPID] or [USER_IDENTIFICATION_CUSTOMER_ID], AppsFlyer customer user ID
* is synced on identity changes and `setUserIdentity(CustomerId)` does not override MPID mode.
*/
const val USER_IDENTIFICATION_TYPE = "userIdentificationType"

const val USER_IDENTIFICATION_MPID = "MPID"
const val USER_IDENTIFICATION_CUSTOMER_ID = "CustomerId"

const val AF_CUSTOMER_USER_ID = "af_customer_user_id"

private const val CONSENT_MAPPING = "consentMapping"

@Suppress("ktlint:standard:property-naming")
Expand Down
32 changes: 32 additions & 0 deletions src/test/kotlin/com/appsflyer/AppsFlyerLib.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.appsflyer

import android.content.Context
import com.appsflyer.AppsFlyerProperties
import java.util.HashMap

class AppsFlyerLib {
private var consentData: AppsFlyerConsent? = null
Expand All @@ -11,6 +13,18 @@ class AppsFlyerLib {
var customerUserId: String? = null
private set

var setCustomerUserIdCallCount = 0
private set

var lastSetCustomerUserId: String? = null
private set

var lastLogEventName: String? = null
private set

var lastLogEventValues: Map<String, Any?>? = null
private set

fun setConsentData(consent: AppsFlyerConsent) {
consentData = consent
}
Expand All @@ -22,9 +36,27 @@ class AppsFlyerLib {
}

fun setCustomerUserId(id: String?) {
setCustomerUserIdCallCount++
lastSetCustomerUserId = id
customerUserId = id
}

fun setUserEmails(
cryptType: AppsFlyerProperties.EmailsCryptType,
vararg emails: String,
) {}

fun logEvent(
context: Context,
name: String,
values: Map<String, Any?>?,
) {
lastLogEventName = name
lastLogEventValues = values?.let { HashMap<String, Any?>(it) }
}

fun anonymizeUser(optOut: Boolean) {}

fun init(
devKey: String,
conversionListener: Any?,
Expand Down
Loading
Loading