Skip to content

Commit 13ea5aa

Browse files
vasctnzagorchev
andauthored
Use loggedInUserId to identify previously logged-in users (#556)
* Use loggedInUserId to identify previously logged-in users on the same device Update CleverTap identification migration * Handle changing the user while offline before getMigrateState --------- Co-authored-by: Nikola Zagorchev <[email protected]>
1 parent 9f84fbd commit 13ea5aa

File tree

9 files changed

+242
-30
lines changed

9 files changed

+242
-30
lines changed

AndroidSDKCore/src/main/java/com/leanplum/internal/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ public static class Params {
168168
public static final String CT_REGION_CODE = "regionCode";
169169
public static final String CT_ATTRIBUTE_MAPPINGS = "attributeMappings";
170170
public static final String CT_IDENTITY_KEYS = "identityKeys";
171+
public static final String LOGGED_IN_USER_ID = "loggedInUserId";
171172
public static final String API_EVENTS_STATE = "events";
172173
}
173174

AndroidSDKCore/src/main/java/com/leanplum/migration/MigrationManager.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121

2222
package com.leanplum.migration
2323

24+
import com.leanplum.Leanplum
2425
import com.leanplum.callbacks.CleverTapInstanceCallback
26+
import com.leanplum.callbacks.StartCallback
2527
import com.leanplum.core.BuildConfig
2628
import com.leanplum.internal.*
2729
import com.leanplum.migration.model.MigrationConfig
@@ -106,6 +108,16 @@ object MigrationManager {
106108
Log.d("Migration state response: $it")
107109
val responseData = ResponseHandler().handleMigrateStateContent(it)
108110
if (responseData != null) {
111+
if (!responseData.loggedInUserId.isNullOrBlank()) {
112+
Leanplum.addStartResponseHandler(object : StartCallback() {
113+
override fun onResponse(success: Boolean) {
114+
if (Leanplum.getUserId() == Leanplum.getDeviceId()) {
115+
Log.d("Leanplum.setUserId() with loggedInUserId: ${responseData.loggedInUserId}")
116+
Leanplum.setUserId(responseData.loggedInUserId)
117+
}
118+
}
119+
})
120+
}
109121
val oldState = getState()
110122
MigrationConfig.update(responseData)
111123
val newState = getState()

AndroidSDKCore/src/main/java/com/leanplum/migration/ResponseHandler.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
package com.leanplum.migration
2323

24+
import com.leanplum.Leanplum
2425
import com.leanplum.internal.Constants.Params
2526
import com.leanplum.internal.Log
2627
import com.leanplum.migration.model.MigrationState
@@ -49,6 +50,7 @@ class ResponseHandler {
4950
* ]
5051
* },
5152
* "sha256": "...",
53+
* "loggedInUserId": "...",
5254
* "success": true,
5355
* "profileUploadStartedTs": "...",
5456
* "eventsUploadStartedTs": "...",
@@ -68,6 +70,11 @@ class ResponseHandler {
6870
if (!json.isNull(Params.SDK)) {
6971
val state = json.getString(Params.SDK)
7072
val hash = json.getString(Params.MIGRATE_STATE_HASH)
73+
val loggedInUserId = if (json.has(Params.LOGGED_IN_USER_ID)) {
74+
json.getString(Params.LOGGED_IN_USER_ID)
75+
} else {
76+
null
77+
}
7178
return if (MigrationState.from(state).useCleverTap()) {
7279
var accountId: String? = null
7380
var token: String? = null
@@ -91,9 +98,9 @@ class ResponseHandler {
9198
}
9299
}
93100
}
94-
ResponseData(state, hash, accountId, token, regionCode, attributeMappings, identityKeysCsv)
101+
ResponseData(state, hash, loggedInUserId, accountId, token, regionCode, attributeMappings, identityKeysCsv)
95102
} else {
96-
ResponseData(state, hash)
103+
ResponseData(state, hash, loggedInUserId)
97104
}
98105
}
99106
} catch (e: JSONException) {

AndroidSDKCore/src/main/java/com/leanplum/migration/model/MigrationConfig.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ object MigrationConfig {
3434
var hash: String by StringPreference(key = "ct_config_hash", defaultValue = "defaultHash")
3535
private set
3636

37+
var loggedInUserId: String? by StringPreferenceNullable(key = "loggedInUserId")
38+
private set
39+
3740
var accountId: String? by StringPreferenceNullable(key = "ct_account_id")
3841
private set
3942

@@ -72,6 +75,7 @@ object MigrationConfig {
7275
fun update(data: ResponseData) {
7376
state = data.state
7477
hash = data.hash
78+
loggedInUserId = data.loggedInUserId
7579
accountId = data.accountId
7680
accountToken = data.token
7781
accountRegion = data.regionCode

AndroidSDKCore/src/main/java/com/leanplum/migration/model/ResponseData.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ package com.leanplum.migration.model
2424
data class ResponseData(
2525
val state: String,
2626
val hash: String,
27+
val loggedInUserId: String? = null,
2728
val accountId: String? = null,
2829
val token: String? = null,
2930
val regionCode: String? = null,

AndroidSDKCore/src/main/java/com/leanplum/migration/wrapper/CTWrapper.kt

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ internal class CTWrapper(
4646
private val accountToken: String,
4747
private val accountRegion: String,
4848
private val identityList: List<String>?,
49+
private val useCustomCleverTapId: Boolean,
4950
deviceId: String,
50-
userId: String?
51+
userId: String?,
52+
loggedInUserId: String?,
5153
) : IWrapper by StaticMethodsWrapper {
5254

5355
override val fcmHandler: FcmMigrationHandler = FcmMigrationHandler()
@@ -57,7 +59,7 @@ internal class CTWrapper(
5759
private var cleverTapInstance: CleverTapAPI? = null
5860
private var instanceCallbackList: MutableList<CleverTapInstanceCallback> = mutableListOf()
5961

60-
private var identityManager = IdentityManager(deviceId, userId ?: deviceId)
62+
private var identityManager = IdentityManager(deviceId, userId ?: deviceId, loggedInUserId)
6163
private var firstTimeStart = identityManager.isFirstTimeStart()
6264

6365
@SuppressLint("WrongConstant")
@@ -72,19 +74,23 @@ internal class CTWrapper(
7274
accountId,
7375
accountToken,
7476
accountRegion).apply {
75-
enableCustomCleverTapId = true
77+
enableCustomCleverTapId = useCustomCleverTapId
7678
debugLevel = ctLevel // set instance log level
7779
setLogLevel(lpLevel) // set static log level, arg needs to be Leanplum's level
7880
identityList?.also { // setting IdentityKeys
7981
setIdentityKeys(*it.toTypedArray())
8082
}
8183
}
8284

83-
val cleverTapId = identityManager.cleverTapId()
84-
val profile = identityManager.profile()
85-
Log.d("Wrapper: using CleverTapID=__h$cleverTapId")
86-
87-
cleverTapInstance = CleverTapAPI.instanceWithConfig(context, config, cleverTapId)?.apply {
85+
cleverTapInstance = if (useCustomCleverTapId) {
86+
val cleverTapId = identityManager.cleverTapId()
87+
Log.d("Wrapper: using CleverTapID=__h$cleverTapId")
88+
CleverTapAPI.instanceWithConfig(context, config, cleverTapId)
89+
} else {
90+
Log.d("Wrapper: without CleverTapID")
91+
CleverTapAPI.instanceWithConfig(context, config)
92+
}
93+
cleverTapInstance?.apply {
8894
setLibrary("Leanplum")
8995
if (!ActivityLifecycleCallback.registered) {
9096
ActivityLifecycleCallback.register(context.applicationContext as? Application)
@@ -93,8 +99,7 @@ internal class CTWrapper(
9399
Log.d("Wrapper: identity not set for anonymous user")
94100
setAnonymousDeviceProperty()
95101
} else {
96-
Log.d("Wrapper: will call onUserLogin with $profile and __h$cleverTapId")
97-
onUserLogin(profile, cleverTapId)
102+
onUserLogin()
98103
setDevicesProperty()
99104
}
100105
Log.d("Wrapper: CleverTap instance created by Leanplum")
@@ -172,13 +177,21 @@ internal class CTWrapper(
172177
// trying to set same userId
173178
return
174179
}
180+
onUserLogin()
181+
cleverTapInstance?.setDevicesProperty()
182+
}
175183

184+
private fun onUserLogin() {
176185
val cleverTapId = identityManager.cleverTapId()
177186
val profile = identityManager.profile()
178187

179-
Log.d("Wrapper: Leanplum.setUserId will call onUserLogin with $profile and __h$cleverTapId")
180-
cleverTapInstance?.onUserLogin(profile, cleverTapId)
181-
cleverTapInstance?.setDevicesProperty()
188+
if (useCustomCleverTapId) {
189+
Log.d("Wrapper: Leanplum.setUserId will call onUserLogin with $profile and __h$cleverTapId")
190+
cleverTapInstance?.onUserLogin(profile, cleverTapId)
191+
} else {
192+
Log.d("Wrapper: Leanplum.setUserId will call onUserLogin with $profile")
193+
cleverTapInstance?.onUserLogin(profile)
194+
}
182195
}
183196

184197
private fun CleverTapAPI.setAnonymousDeviceProperty() {

AndroidSDKCore/src/main/java/com/leanplum/migration/wrapper/IdentityManager.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ private const val IDENTIFIED = "identified"
6363
class IdentityManager(
6464
deviceId: String,
6565
userId: String,
66+
loggedInUserId: String? = null,
6667
stateDelegate: ReadWriteProperty<Any, String> = StringPreference("ct_login_state", UNDEFINED),
6768
mergeUserDelegate: ReadWriteProperty<Any, String?> = StringPreferenceNullable("ct_anon_merge_userid"),
6869
) {
@@ -75,44 +76,54 @@ class IdentityManager(
7576
init {
7677
startState = state
7778
if (isAnonymous()) {
78-
loginAnonymously()
79+
if (loggedInUserId != null) {
80+
loginWithLoggedInUserId(loggedInUserId)
81+
} else {
82+
loginAnonymously()
83+
}
7984
} else {
8085
loginIdentified()
8186
}
8287
}
8388

84-
fun isAnonymous() = identity.isAnonymous()
89+
fun isAnonymous() = identity.isAnonymous() && state != IDENTIFIED
8590

8691
fun isFirstTimeStart() = startState == UNDEFINED
8792

93+
private fun loginWithLoggedInUserId(loggedInUserId: String) {
94+
identity.setUserId(loggedInUserId)
95+
state = IDENTIFIED
96+
}
97+
8898
private fun loginAnonymously() {
8999
state = ANONYMOUS
90100
}
91101

92102
private fun loginIdentified() {
93103
if (state == UNDEFINED) {
94104
state = IDENTIFIED
95-
}
96-
else if (state == ANONYMOUS) {
105+
} else if (state == ANONYMOUS) {
97106
anonymousMergeUserId = identity.userId()
98107
Log.d("Wrapper: anonymous data will be merged to $anonymousMergeUserId")
99108
state = IDENTIFIED
100109
}
101110
}
102111

103112
fun cleverTapId(): String {
104-
if (identity.isAnonymous()) {
105-
return identity.deviceId()
106-
} else if (identity.userId() == anonymousMergeUserId) {
107-
return identity.deviceId()
113+
return if (identity.userId() != anonymousMergeUserId && (!identity.isAnonymous() || state == IDENTIFIED)) {
114+
"${identity.deviceId()}_${identity.userId()}"
108115
} else {
109-
return "${identity.deviceId()}_${identity.userId()}"
116+
identity.deviceId()
110117
}
111118
}
112119

113120
fun profile() = mapOf(MigrationConstants.IDENTITY to identity.originalUserId())
114121

115122
fun setUserId(userId: String): Boolean {
123+
if (userId == identity.originalDeviceId() && state == ANONYMOUS) {
124+
return false
125+
}
126+
116127
if (!identity.setUserId(userId)) {
117128
// trying to set same userId
118129
return false
@@ -123,7 +134,7 @@ class IdentityManager(
123134
Log.d("Wrapper: anonymous data will be merged to $anonymousMergeUserId")
124135
state = IDENTIFIED
125136
}
126-
return true;
137+
return true
127138
}
128139

129140
fun isDeviceIdHashed() = identity.originalDeviceId() != identity.deviceId()

AndroidSDKCore/src/main/java/com/leanplum/migration/wrapper/WrapperFactory.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import com.leanplum.internal.AESCrypt
2828
import com.leanplum.internal.Constants
2929
import com.leanplum.internal.LeanplumInternal
3030
import com.leanplum.internal.Log
31+
import com.leanplum.migration.MigrationManager
3132
import com.leanplum.migration.model.MigrationConfig
33+
import com.leanplum.migration.model.MigrationState
3234
import com.leanplum.utils.getLeanplumPrefs
3335
import kotlin.system.measureTimeMillis
3436

@@ -61,7 +63,16 @@ internal object WrapperFactory {
6163

6264
val identityList = MigrationConfig.identityList
6365

64-
return CTWrapper(account, token, region, identityList, deviceId, userId).apply {
66+
return CTWrapper(
67+
account,
68+
token,
69+
region,
70+
identityList,
71+
MigrationManager.getState() != MigrationState.CleverTapOnly,
72+
deviceId,
73+
userId,
74+
MigrationConfig.loggedInUserId
75+
).apply {
6576
val timeToLaunch = measureTimeMillis {
6677
launch(context, callbacks)
6778
}

0 commit comments

Comments
 (0)