Skip to content

Commit e58e6e0

Browse files
feat: Support UserIdentificationType (#119)
1 parent ff3596b commit e58e6e0

3 files changed

Lines changed: 290 additions & 45 deletions

File tree

src/main/kotlin/com/mparticle/kits/AppsFlyerKit.kt

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.mparticle.MParticle
2929
import com.mparticle.commerce.CommerceEvent
3030
import com.mparticle.commerce.Product
3131
import com.mparticle.consent.ConsentState
32+
import com.mparticle.identity.MParticleUser
3233
import com.mparticle.internal.Logger
3334
import com.mparticle.internal.MPUtility
3435
import org.json.JSONArray
@@ -47,7 +48,8 @@ class AppsFlyerKit :
4748
KitIntegration.CommerceListener,
4849
AppsFlyerConversionListener,
4950
KitIntegration.ActivityListener,
50-
KitIntegration.UserAttributeListener {
51+
KitIntegration.UserAttributeListener,
52+
KitIntegration.IdentityListener {
5153
override fun getInstance(): AppsFlyerLib = AppsFlyerLib.getInstance()
5254

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

79+
updateCustomerUserIDIfNeededForUser(currentUser)
80+
7781
val messages: MutableList<ReportingMessage> = ArrayList()
7882
messages.add(
7983
ReportingMessage(
@@ -107,6 +111,7 @@ class AppsFlyerKit :
107111
): List<ReportingMessage> = emptyList()
108112

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

217222
override fun logEvent(event: MPEvent): List<ReportingMessage> {
218-
var hashMap: HashMap<String?, Any?>? = hashMapOf()
219-
if (event.customAttributes?.isNotEmpty() == true) {
220-
hashMap = event.customAttributes?.let { HashMap(it) }
223+
val hashMap: HashMap<String?, Any?> =
224+
event.customAttributes
225+
?.takeIf { it.isNotEmpty() }
226+
?.let { HashMap(it) }
227+
?: hashMapOf()
228+
customerIdForAppsFlyer(currentUser)?.takeIf { it.isNotEmpty() }?.let { cid ->
229+
hashMap[AF_CUSTOMER_USER_ID] = cid
221230
}
222231
instance.logEvent(context, event.eventName, hashMap)
223232
val messages: MutableList<ReportingMessage> = LinkedList()
@@ -307,7 +316,11 @@ class AppsFlyerKit :
307316
override fun removeUserIdentity(identityType: MParticle.IdentityType) {
308317
with(instance) {
309318
if (MParticle.IdentityType.CustomerId == identityType) {
310-
setCustomerUserId("")
319+
if (isUserIdentificationMPID() || isUserIdentificationCustomerId()) {
320+
updateCustomerUserIDIfNeededForUser(currentUser)
321+
} else {
322+
setCustomerUserId("")
323+
}
311324
} else if (MParticle.IdentityType.Email == identityType) {
312325
setUserEmails(AppsFlyerProperties.EmailsCryptType.NONE, "")
313326
}
@@ -320,7 +333,9 @@ class AppsFlyerKit :
320333
) {
321334
with(instance) {
322335
if (MParticle.IdentityType.CustomerId == identityType) {
323-
setCustomerUserId(identity)
336+
if (!isUserIdentificationMPID()) {
337+
setCustomerUserId(identity)
338+
}
324339
} else if (MParticle.IdentityType.Email == identityType) {
325340
setUserEmails(AppsFlyerProperties.EmailsCryptType.NONE, identity)
326341
}
@@ -329,6 +344,71 @@ class AppsFlyerKit :
329344

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

347+
override fun onIdentifyCompleted(
348+
mParticleUser: MParticleUser,
349+
identityApiRequest: FilteredIdentityApiRequest,
350+
) {
351+
updateCustomerUserIDIfNeededForUser(mParticleUser)
352+
}
353+
354+
override fun onLoginCompleted(
355+
mParticleUser: MParticleUser,
356+
identityApiRequest: FilteredIdentityApiRequest,
357+
) {
358+
updateCustomerUserIDIfNeededForUser(mParticleUser)
359+
}
360+
361+
override fun onLogoutCompleted(
362+
mParticleUser: MParticleUser,
363+
identityApiRequest: FilteredIdentityApiRequest,
364+
) {
365+
updateCustomerUserIDIfNeededForUser(mParticleUser)
366+
}
367+
368+
override fun onModifyCompleted(
369+
mParticleUser: MParticleUser,
370+
identityApiRequest: FilteredIdentityApiRequest,
371+
) {
372+
updateCustomerUserIDIfNeededForUser(mParticleUser)
373+
}
374+
375+
override fun onUserIdentified(mParticleUser: MParticleUser) {
376+
updateCustomerUserIDIfNeededForUser(mParticleUser)
377+
}
378+
379+
private fun isUserIdentificationMPID(): Boolean = USER_IDENTIFICATION_MPID == settings[USER_IDENTIFICATION_TYPE]
380+
381+
private fun isUserIdentificationCustomerId(): Boolean = USER_IDENTIFICATION_CUSTOMER_ID == settings[USER_IDENTIFICATION_TYPE]
382+
383+
private fun customerIdForAppsFlyer(user: MParticleUser?): String? {
384+
if (user == null) {
385+
return null
386+
}
387+
return when {
388+
isUserIdentificationMPID() -> user.id.toString()
389+
isUserIdentificationCustomerId() ->
390+
user.getUserIdentities()[MParticle.IdentityType.CustomerId]
391+
else -> user.id.toString()
392+
}
393+
}
394+
395+
private fun updateCustomerUserIDIfNeededForUser(user: MParticleUser?) {
396+
if (!isUserIdentificationMPID() && !isUserIdentificationCustomerId()) {
397+
return
398+
}
399+
instance.setCustomerUserId(customerIdForAppsFlyer(user))
400+
}
401+
402+
private fun commerceEventWithAppsFlyerCustomerUserId(event: CommerceEvent): CommerceEvent {
403+
val cid = customerIdForAppsFlyer(currentUser)?.takeIf { it.isNotEmpty() } ?: return event
404+
val newAttrs = HashMap<String, String>()
405+
event.customAttributes?.forEach { (key, value) ->
406+
newAttrs[key] = value?.toString() ?: ""
407+
}
408+
newAttrs[AF_CUSTOMER_USER_ID] = cid
409+
return CommerceEvent.Builder(event).customAttributes(newAttrs).build()
410+
}
411+
332412
private fun parseToNestedMap(jsonString: String): Map<String, Any> {
333413
val topLevelMap = mutableMapOf<String, Any>()
334414
try {
@@ -608,6 +688,19 @@ class AppsFlyerKit :
608688
}
609689

610690
const val MANUAL_START = "manualStart"
691+
692+
/**
693+
* Kit setting: use [USER_IDENTIFICATION_MPID], [USER_IDENTIFICATION_CUSTOMER_ID], or omit for legacy (MPID).
694+
* When set to [USER_IDENTIFICATION_MPID] or [USER_IDENTIFICATION_CUSTOMER_ID], AppsFlyer customer user ID
695+
* is synced on identity changes and `setUserIdentity(CustomerId)` does not override MPID mode.
696+
*/
697+
const val USER_IDENTIFICATION_TYPE = "userIdentificationType"
698+
699+
const val USER_IDENTIFICATION_MPID = "MPID"
700+
const val USER_IDENTIFICATION_CUSTOMER_ID = "CustomerId"
701+
702+
const val AF_CUSTOMER_USER_ID = "af_customer_user_id"
703+
611704
private const val CONSENT_MAPPING = "consentMapping"
612705

613706
@Suppress("ktlint:standard:property-naming")

src/test/kotlin/com/appsflyer/AppsFlyerLib.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.appsflyer
22

33
import android.content.Context
4+
import com.appsflyer.AppsFlyerProperties
5+
import java.util.HashMap
46

57
class AppsFlyerLib {
68
private var consentData: AppsFlyerConsent? = null
@@ -11,6 +13,18 @@ class AppsFlyerLib {
1113
var customerUserId: String? = null
1214
private set
1315

16+
var setCustomerUserIdCallCount = 0
17+
private set
18+
19+
var lastSetCustomerUserId: String? = null
20+
private set
21+
22+
var lastLogEventName: String? = null
23+
private set
24+
25+
var lastLogEventValues: Map<String, Any?>? = null
26+
private set
27+
1428
fun setConsentData(consent: AppsFlyerConsent) {
1529
consentData = consent
1630
}
@@ -22,9 +36,27 @@ class AppsFlyerLib {
2236
}
2337

2438
fun setCustomerUserId(id: String?) {
39+
setCustomerUserIdCallCount++
40+
lastSetCustomerUserId = id
2541
customerUserId = id
2642
}
2743

44+
fun setUserEmails(
45+
cryptType: AppsFlyerProperties.EmailsCryptType,
46+
vararg emails: String,
47+
) {}
48+
49+
fun logEvent(
50+
context: Context,
51+
name: String,
52+
values: Map<String, Any?>?,
53+
) {
54+
lastLogEventName = name
55+
lastLogEventValues = values?.let { HashMap<String, Any?>(it) }
56+
}
57+
58+
fun anonymizeUser(optOut: Boolean) {}
59+
2860
fun init(
2961
devKey: String,
3062
conversionListener: Any?,

0 commit comments

Comments
 (0)