From 30e3a685ba31da222e1a9e11372fbc044c9e61a7 Mon Sep 17 00:00:00 2001 From: Neelkanth Kaushik Date: Fri, 27 Jun 2025 11:48:28 +0530 Subject: [PATCH 1/3] Updated code for deep links should have context:referrer like Swift sdk --- .../kotlin/android/utilities/DeepLinkUtils.kt | 1 + .../analytics/kotlin/android/StorageTests.kt | 6 ++-- .../analytics/kotlin/core/Analytics.kt | 12 ++++++- .../segment/analytics/kotlin/core/State.kt | 27 ++++++++++---- .../core/platform/plugins/ContextPlugin.kt | 11 +++++- .../analytics/kotlin/core/AnalyticsTests.kt | 35 +++++++++++++++---- .../analytics/kotlin/core/StateTest.kt | 21 +++++++++++ .../core/utilities/InMemoryStorageTest.kt | 6 ++-- .../kotlin/core/utilities/StorageImplTest.kt | 9 +++-- 9 files changed, 106 insertions(+), 22 deletions(-) diff --git a/android/src/main/java/com/segment/analytics/kotlin/android/utilities/DeepLinkUtils.kt b/android/src/main/java/com/segment/analytics/kotlin/android/utilities/DeepLinkUtils.kt index c551b088..345b8dbb 100644 --- a/android/src/main/java/com/segment/analytics/kotlin/android/utilities/DeepLinkUtils.kt +++ b/android/src/main/java/com/segment/analytics/kotlin/android/utilities/DeepLinkUtils.kt @@ -15,6 +15,7 @@ class DeepLinkUtils(val analytics: Analytics) { return } + analytics.updateReferrer(intent.data.toString()) val properties = extractLinkProperties(referrer, intent.data) analytics.track("Deep Link Opened", properties) } diff --git a/android/src/test/java/com/segment/analytics/kotlin/android/StorageTests.kt b/android/src/test/java/com/segment/analytics/kotlin/android/StorageTests.kt index 9245434d..72e49da0 100644 --- a/android/src/test/java/com/segment/analytics/kotlin/android/StorageTests.kt +++ b/android/src/test/java/com/segment/analytics/kotlin/android/StorageTests.kt @@ -55,7 +55,8 @@ class StorageTests { UserInfo( anonymousId = "oldAnonId", userId = "oldUserId", - traits = buildJsonObject { put("behaviour", "bad") } + traits = buildJsonObject { put("behaviour", "bad") }, + referrer = "oldReferrer", )) store.provide( @@ -85,7 +86,8 @@ class StorageTests { return UserInfo( anonymousId = "newAnonId", userId = "newUserId", - traits = emptyJsonObject + traits = emptyJsonObject, + referrer = "newReferrer" ) } } diff --git a/core/src/main/java/com/segment/analytics/kotlin/core/Analytics.kt b/core/src/main/java/com/segment/analytics/kotlin/core/Analytics.kt index 0975d1eb..60c96877 100644 --- a/core/src/main/java/com/segment/analytics/kotlin/core/Analytics.kt +++ b/core/src/main/java/com/segment/analytics/kotlin/core/Analytics.kt @@ -580,7 +580,7 @@ open class Analytics protected constructor( */ fun reset() { val newAnonymousId = UUID.randomUUID().toString() - userInfo = UserInfo(newAnonymousId, null, null) + userInfo = UserInfo(newAnonymousId, null, null, null) analyticsScope.launch(analyticsDispatcher) { store.dispatch(UserInfo.ResetAction(newAnonymousId), UserInfo::class) @@ -740,6 +740,16 @@ open class Analytics protected constructor( catch (ignored: Exception) {} } } + + fun updateReferrer(referrerUrl: String?) { + userInfo.referrer = referrerUrl + analyticsScope.launch { + store.dispatch( + UserInfo.SetReferrerAction(referrerUrl), + UserInfo::class + ) + } + } } diff --git a/core/src/main/java/com/segment/analytics/kotlin/core/State.kt b/core/src/main/java/com/segment/analytics/kotlin/core/State.kt index 374c56c5..31c8405e 100644 --- a/core/src/main/java/com/segment/analytics/kotlin/core/State.kt +++ b/core/src/main/java/com/segment/analytics/kotlin/core/State.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put import sovran.kotlin.Action import sovran.kotlin.State -import java.util.* +import java.util.UUID /** * Stores state related to the analytics system @@ -143,11 +143,13 @@ data class System( * - anonymousId (String) * - userId (String) * - traits (Map) + * - referrer (String) */ data class UserInfo( var anonymousId: String, var userId: String?, - var traits: JsonObject? + var traits: JsonObject?, + var referrer: String?, ) : State { companion object { @@ -160,25 +162,33 @@ data class UserInfo( val anonymousId: String = storage.read(Storage.Constants.AnonymousId) ?: UUID.randomUUID().toString() - return UserInfo(anonymousId, userId, traits) + return UserInfo(anonymousId, userId, traits, null) } } class ResetAction(var anonymousId: String = UUID.randomUUID().toString()) : Action { override fun reduce(state: UserInfo): UserInfo { - return UserInfo(anonymousId, null, null) + return UserInfo(anonymousId, null, null, null) } } class SetUserIdAction(var userId: String) : Action { override fun reduce(state: UserInfo): UserInfo { - return UserInfo(state.anonymousId, userId, state.traits) + return state.copy( + userId = userId + ) } } class SetAnonymousIdAction(var anonymousId: String) : Action { override fun reduce(state: UserInfo): UserInfo { - return UserInfo(anonymousId, state.userId, state.traits) + return state.copy(anonymousId = anonymousId) + } + } + + class SetReferrerAction(var referrer: String?) : Action { + override fun reduce(state: UserInfo): UserInfo { + return state.copy(referrer = referrer) } } @@ -190,7 +200,10 @@ data class UserInfo( class SetUserIdAndTraitsAction(var userId: String, var traits: JsonObject) : Action { override fun reduce(state: UserInfo): UserInfo { - return UserInfo(state.anonymousId, userId, traits) + return state.copy( + userId = userId, + traits = traits + ) } } } diff --git a/core/src/main/java/com/segment/analytics/kotlin/core/platform/plugins/ContextPlugin.kt b/core/src/main/java/com/segment/analytics/kotlin/core/platform/plugins/ContextPlugin.kt index d7e7d1d8..f497e1fc 100644 --- a/core/src/main/java/com/segment/analytics/kotlin/core/platform/plugins/ContextPlugin.kt +++ b/core/src/main/java/com/segment/analytics/kotlin/core/platform/plugins/ContextPlugin.kt @@ -8,7 +8,7 @@ import com.segment.analytics.kotlin.core.utilities.putAll import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put -import java.util.* +import java.util.UUID /** * Analytics plugin used to populate events with basic context data. @@ -27,6 +27,8 @@ class ContextPlugin : Plugin { const val LIBRARY_NAME_KEY = "name" const val LIBRARY_VERSION_KEY = "version" const val INSTANCE_ID_KEY = "instanceId" + const val REFERRER_KEY = "referrer" + const val URL_KEY = "url" } override fun setup(analytics: Analytics) { @@ -45,6 +47,13 @@ class ContextPlugin : Plugin { // putLibrary put(LIBRARY_KEY, library) put(INSTANCE_ID_KEY, instanceId) + + // add additional context data + analytics.userInfo.referrer?.let { referrer -> + put(REFERRER_KEY, buildJsonObject { + put(URL_KEY, referrer) + }) + } } event.context = newContext } diff --git a/core/src/test/kotlin/com/segment/analytics/kotlin/core/AnalyticsTests.kt b/core/src/test/kotlin/com/segment/analytics/kotlin/core/AnalyticsTests.kt index 29c78763..7b650295 100644 --- a/core/src/test/kotlin/com/segment/analytics/kotlin/core/AnalyticsTests.kt +++ b/core/src/test/kotlin/com/segment/analytics/kotlin/core/AnalyticsTests.kt @@ -472,8 +472,8 @@ class AnalyticsTests { UserInfo( userId = "oldUserId", traits = buildJsonObject { put("behaviour", "bad") }, - anonymousId = "qwerty-qwerty-123" - ), curUserInfo + anonymousId = "qwerty-qwerty-123", + referrer = null), curUserInfo ) analytics.identify("newUserId", buildJsonObject { put("behaviour", "good") }) @@ -483,7 +483,8 @@ class AnalyticsTests { UserInfo( userId = "newUserId", traits = buildJsonObject { put("behaviour", "good") }, - anonymousId = "qwerty-qwerty-123" + anonymousId = "qwerty-qwerty-123", + referrer = null ), newUserInfo ) } @@ -501,7 +502,8 @@ class AnalyticsTests { UserInfo( userId = "oldUserId", traits = buildJsonObject { put("behaviour", "bad") }, - anonymousId = "qwerty-qwerty-123" + anonymousId = "qwerty-qwerty-123", + referrer = null ), curUserInfo ) @@ -512,7 +514,8 @@ class AnalyticsTests { UserInfo( userId = "oldUserId", traits = buildJsonObject { put("behaviour", "good") }, - anonymousId = "qwerty-qwerty-123" + anonymousId = "qwerty-qwerty-123", + referrer = null ), newUserInfo ) } @@ -782,7 +785,8 @@ class AnalyticsTests { UserInfo( userId = "newId", traits = emptyJsonObject, - anonymousId = "qwerty-qwerty-123" + anonymousId = "qwerty-qwerty-123", + referrer = null ), newUserInfo ) } @@ -890,6 +894,25 @@ class AnalyticsTests { } } + @Nested + inner class Referrer { + @Test + fun `updateReferrer non-null update`() = runTest { + analytics.updateReferrer("https://example.com") + val newUserInfo = analytics.store.currentState(UserInfo::class) + assertEquals(analytics.userInfo.referrer, "https://example.com") + assertEquals(newUserInfo?.referrer, "https://example.com") + } + + @Test + fun `updateReferrer null update`() = runTest { + analytics.updateReferrer(null) + val newUserInfo = analytics.store.currentState(UserInfo::class) + assertEquals(analytics.userInfo.referrer, null) + assertEquals(newUserInfo?.referrer, null) + } + } + @Test fun `settings fetches current Analytics Settings`() = runTest { val settings = Settings( diff --git a/core/src/test/kotlin/com/segment/analytics/kotlin/core/StateTest.kt b/core/src/test/kotlin/com/segment/analytics/kotlin/core/StateTest.kt index 4f4257da..52d535fc 100644 --- a/core/src/test/kotlin/com/segment/analytics/kotlin/core/StateTest.kt +++ b/core/src/test/kotlin/com/segment/analytics/kotlin/core/StateTest.kt @@ -99,4 +99,25 @@ internal class StateTest { assertEquals(traits, (analytics.store.currentState(UserInfo::class))?.traits) } } + + @Test + fun setReferrerAction() = runTest { + analytics.store.dispatch(UserInfo.SetReferrerAction("oldReferrer"), UserInfo::class) + assertEquals("oldReferrer", (analytics.store.currentState(UserInfo::class))?.referrer) + + analytics.store.dispatch(UserInfo.SetReferrerAction("newReferrer"), UserInfo::class) + assertEquals("newReferrer", (analytics.store.currentState(UserInfo::class))?.referrer) + } + + + @Test + fun `setReferrerAction state change`() = runTest { + val traits = buildJsonObject { put("behaviour", "bad") } + analytics.store.dispatch(UserInfo.SetReferrerAction("newReferrer"), UserInfo::class) + analytics.store.dispatch(UserInfo.SetUserIdAction("oldUserId"), UserInfo::class) + analytics.store.dispatch(UserInfo.SetAnonymousIdAction("anonymous"), UserInfo::class) + analytics.store.dispatch(UserInfo.SetTraitsAction(traits), UserInfo::class) + + assertEquals("newReferrer", (analytics.store.currentState(UserInfo::class))?.referrer) + } } \ No newline at end of file diff --git a/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/InMemoryStorageTest.kt b/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/InMemoryStorageTest.kt index aafe2c8d..b958d005 100644 --- a/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/InMemoryStorageTest.kt +++ b/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/InMemoryStorageTest.kt @@ -61,7 +61,8 @@ internal class InMemoryStorageTest { return UserInfo( anonymousId = "newAnonId", userId = "newUserId", - traits = emptyJsonObject + traits = emptyJsonObject, + referrer = "newReferrer" ) } } @@ -294,7 +295,8 @@ internal class InMemoryStorageTest { return UserInfo( anonymousId = "newAnonId", userId = "newUserId", - traits = emptyJsonObject + traits = emptyJsonObject, + referrer = "newReferrer" ) } } diff --git a/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/StorageImplTest.kt b/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/StorageImplTest.kt index f8f8efa1..7a2f97a9 100644 --- a/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/StorageImplTest.kt +++ b/core/src/test/kotlin/com/segment/analytics/kotlin/core/utilities/StorageImplTest.kt @@ -48,7 +48,8 @@ internal class StorageImplTest { UserInfo( anonymousId = "oldAnonId", userId = "oldUserId", - traits = buildJsonObject { put("behaviour", "bad") } + traits = buildJsonObject { put("behaviour", "bad") }, + referrer = "oldReferrer" )) store.provide( @@ -86,7 +87,8 @@ internal class StorageImplTest { return UserInfo( anonymousId = "newAnonId", userId = "newUserId", - traits = emptyJsonObject + traits = emptyJsonObject, + referrer = "newReferrer" ) } } @@ -341,7 +343,8 @@ internal class StorageImplTest { return UserInfo( anonymousId = "newAnonId", userId = "newUserId", - traits = emptyJsonObject + traits = emptyJsonObject, + referrer = "newReferrer" ) } } From a1f3ad6a8371f9023ffd1c4c3ef2da2bc6701ee5 Mon Sep 17 00:00:00 2001 From: Neelkanth Kaushik Date: Fri, 27 Jun 2025 12:35:36 +0530 Subject: [PATCH 2/3] Updated code for deep links should have context:referrer like Swift sdk --- core/src/main/java/com/segment/analytics/kotlin/core/State.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/segment/analytics/kotlin/core/State.kt b/core/src/main/java/com/segment/analytics/kotlin/core/State.kt index 31c8405e..d49946a2 100644 --- a/core/src/main/java/com/segment/analytics/kotlin/core/State.kt +++ b/core/src/main/java/com/segment/analytics/kotlin/core/State.kt @@ -194,7 +194,7 @@ data class UserInfo( class SetTraitsAction(var traits: JsonObject) : Action { override fun reduce(state: UserInfo): UserInfo { - return UserInfo(state.anonymousId, state.userId, traits) + return state.copy(traits = traits) } } From b5266cac356070e6d092bf74ac781bc3135f28ab Mon Sep 17 00:00:00 2001 From: Neelkanth Kaushik Date: Fri, 27 Jun 2025 13:09:27 +0530 Subject: [PATCH 3/3] Updated the version --- .../main/java/com/segment/analytics/kotlin/core/Constants.kt | 2 +- gradle.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/segment/analytics/kotlin/core/Constants.kt b/core/src/main/java/com/segment/analytics/kotlin/core/Constants.kt index d99efefe..c3d32ff5 100644 --- a/core/src/main/java/com/segment/analytics/kotlin/core/Constants.kt +++ b/core/src/main/java/com/segment/analytics/kotlin/core/Constants.kt @@ -1,7 +1,7 @@ package com.segment.analytics.kotlin.core object Constants { - const val LIBRARY_VERSION = "1.20.0" + const val LIBRARY_VERSION = "1.20.1" const val DEFAULT_API_HOST = "api.segment.io/v1" const val DEFAULT_CDN_HOST = "cdn-settings.segment.com/v1" } diff --git a/gradle.properties b/gradle.properties index 08db8e59..b7bde435 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,8 +24,8 @@ android.disableAutomaticComponentCreation=true # Deployment variables GROUP=com.segment.analytics.kotlin -VERSION_CODE=1200 -VERSION_NAME=1.20.0 +VERSION_CODE=1201 +VERSION_NAME=1.20.1 POM_NAME=Segment for Kotlin POM_DESCRIPTION=The hassle-free way to add analytics to your Kotlin app.