Add super properties to posthog (plateformCode)
This commit is contained in:
parent
4acbe4e582
commit
08c124e13b
|
@ -160,7 +160,7 @@ dependencies {
|
||||||
api 'com.facebook.stetho:stetho:1.6.0'
|
api 'com.facebook.stetho:stetho:1.6.0'
|
||||||
|
|
||||||
// Analytics
|
// Analytics
|
||||||
api 'com.github.matrix-org:matrix-analytics-events:0.15.0'
|
api 'com.github.matrix-org:matrix-analytics-events:0.22.0'
|
||||||
|
|
||||||
api libs.google.phonenumber
|
api libs.google.phonenumber
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,8 @@ import im.vector.app.core.dispatchers.CoroutineDispatchers
|
||||||
import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase
|
import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase
|
||||||
import im.vector.app.core.services.GuardServiceStarter
|
import im.vector.app.core.services.GuardServiceStarter
|
||||||
import im.vector.app.core.session.ConfigureAndStartSessionUseCase
|
import im.vector.app.core.session.ConfigureAndStartSessionUseCase
|
||||||
import im.vector.app.features.analytics.DecryptionFailureTracker
|
import im.vector.app.features.analytics.VectorAnalytics
|
||||||
|
import im.vector.app.features.analytics.plan.SuperProperties
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.crypto.keysrequest.KeyRequestHandler
|
import im.vector.app.features.crypto.keysrequest.KeyRequestHandler
|
||||||
import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler
|
import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler
|
||||||
|
@ -57,7 +58,7 @@ class ActiveSessionHolder @Inject constructor(
|
||||||
private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
|
private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
|
||||||
private val applicationCoroutineScope: CoroutineScope,
|
private val applicationCoroutineScope: CoroutineScope,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val decryptionFailureTracker: DecryptionFailureTracker,
|
private val vectorAnalytics: VectorAnalytics,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private var activeSessionReference: AtomicReference<Session?> = AtomicReference()
|
private var activeSessionReference: AtomicReference<Session?> = AtomicReference()
|
||||||
|
@ -74,6 +75,13 @@ class ActiveSessionHolder @Inject constructor(
|
||||||
session.callSignalingService().addCallListener(callManager)
|
session.callSignalingService().addCallListener(callManager)
|
||||||
imageManager.onSessionStarted(session)
|
imageManager.onSessionStarted(session)
|
||||||
guardServiceStarter.start()
|
guardServiceStarter.start()
|
||||||
|
vectorAnalytics.updateSuperProperties(
|
||||||
|
SuperProperties(
|
||||||
|
platformCodeName = SuperProperties.PlatformCodeName.EA,
|
||||||
|
cryptoSDK = SuperProperties.CryptoSDK.Rust,
|
||||||
|
cryptoSDKVersion = session.cryptoService().getCryptoVersion(applicationContext, false)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clearActiveSession() {
|
suspend fun clearActiveSession() {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.analytics
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
||||||
|
import im.vector.app.features.analytics.plan.SuperProperties
|
||||||
import im.vector.app.features.analytics.plan.UserProperties
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
|
|
||||||
interface AnalyticsTracker {
|
interface AnalyticsTracker {
|
||||||
|
@ -35,4 +36,10 @@ interface AnalyticsTracker {
|
||||||
* Update user specific properties.
|
* Update user specific properties.
|
||||||
*/
|
*/
|
||||||
fun updateUserProperties(userProperties: UserProperties)
|
fun updateUserProperties(userProperties: UserProperties)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the super properties.
|
||||||
|
* Super properties are added to any tracked event automatically.
|
||||||
|
*/
|
||||||
|
fun updateSuperProperties(updatedProperties: SuperProperties)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import im.vector.app.features.analytics.VectorAnalytics
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
||||||
import im.vector.app.features.analytics.log.analyticsTag
|
import im.vector.app.features.analytics.log.analyticsTag
|
||||||
|
import im.vector.app.features.analytics.plan.SuperProperties
|
||||||
import im.vector.app.features.analytics.plan.UserProperties
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
import im.vector.app.features.analytics.store.AnalyticsStore
|
import im.vector.app.features.analytics.store.AnalyticsStore
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -63,6 +64,8 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
// Cache for the properties to send
|
// Cache for the properties to send
|
||||||
private var pendingUserProperties: UserProperties? = null
|
private var pendingUserProperties: UserProperties? = null
|
||||||
|
|
||||||
|
private var superProperties: SuperProperties? = null
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
observeUserConsent()
|
observeUserConsent()
|
||||||
observeAnalyticsId()
|
observeAnalyticsId()
|
||||||
|
@ -173,7 +176,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
?.capture(
|
?.capture(
|
||||||
event.getName(),
|
event.getName(),
|
||||||
analyticsId,
|
analyticsId,
|
||||||
event.getProperties()?.toPostHogProperties()
|
event.getProperties()?.toPostHogProperties().orEmpty().withSuperProperties()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +184,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
Timber.tag(analyticsTag.value).d("screen($screen)")
|
Timber.tag(analyticsTag.value).d("screen($screen)")
|
||||||
posthog
|
posthog
|
||||||
?.takeIf { userConsent == true }
|
?.takeIf { userConsent == true }
|
||||||
?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties())
|
?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties().orEmpty().withSuperProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateUserProperties(userProperties: UserProperties) {
|
override fun updateUserProperties(userProperties: UserProperties) {
|
||||||
|
@ -226,9 +229,38 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
return nonNulls
|
return nonNulls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds super properties to the actual property set.
|
||||||
|
* If a property of the same name is already on the reported event it will not be overwritten.
|
||||||
|
*/
|
||||||
|
private fun Map<String, Any>.withSuperProperties(): Map<String, Any> {
|
||||||
|
val withSuperProperties = this.toMutableMap()
|
||||||
|
val superProperties = this@DefaultVectorAnalytics.superProperties?.getProperties()
|
||||||
|
superProperties?.forEach {
|
||||||
|
if (!withSuperProperties.containsKey(it.key)) {
|
||||||
|
withSuperProperties[it.key] = it.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return withSuperProperties
|
||||||
|
}
|
||||||
|
|
||||||
override fun trackError(throwable: Throwable) {
|
override fun trackError(throwable: Throwable) {
|
||||||
sentryAnalytics
|
sentryAnalytics
|
||||||
.takeIf { userConsent == true }
|
.takeIf { userConsent == true }
|
||||||
?.trackError(throwable)
|
?.trackError(throwable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateSuperProperties(updatedProperties: SuperProperties) {
|
||||||
|
if (this.superProperties == null) {
|
||||||
|
this.superProperties = updatedProperties
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.superProperties = SuperProperties(
|
||||||
|
platformCodeName = updatedProperties.platformCodeName ?: this.superProperties?.platformCodeName,
|
||||||
|
cryptoSDK = updatedProperties.cryptoSDK ?: this.superProperties?.cryptoSDK,
|
||||||
|
appPlatform = updatedProperties.appPlatform ?: this.superProperties?.appPlatform,
|
||||||
|
cryptoSDKVersion = updatedProperties.cryptoSDKVersion ?: superProperties?.cryptoSDKVersion
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.plan.SuperProperties
|
||||||
import im.vector.app.test.fakes.FakeAnalyticsStore
|
import im.vector.app.test.fakes.FakeAnalyticsStore
|
||||||
import im.vector.app.test.fakes.FakeLateInitUserPropertiesFactory
|
import im.vector.app.test.fakes.FakeLateInitUserPropertiesFactory
|
||||||
import im.vector.app.test.fakes.FakePostHog
|
import im.vector.app.test.fakes.FakePostHog
|
||||||
|
@ -174,6 +175,117 @@ class DefaultVectorAnalyticsTest {
|
||||||
fakeSentryAnalytics.verifyNoErrorTracking()
|
fakeSentryAnalytics.verifyNoErrorTracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Super properties should be added to all captured events`() = runTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||||
|
|
||||||
|
val updatedProperties = SuperProperties(
|
||||||
|
platformCodeName = SuperProperties.PlatformCodeName.EA,
|
||||||
|
cryptoSDKVersion = "0.0",
|
||||||
|
cryptoSDK = SuperProperties.CryptoSDK.Rust
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.updateSuperProperties(updatedProperties)
|
||||||
|
|
||||||
|
val fakeEvent = aVectorAnalyticsEvent("THE_NAME", mutableMapOf("foo" to "bar"))
|
||||||
|
defaultVectorAnalytics.capture(fakeEvent)
|
||||||
|
|
||||||
|
fakePostHog.verifyEventTracked(
|
||||||
|
"THE_NAME",
|
||||||
|
fakeEvent.getProperties().clearNulls()?.toMutableMap()?.apply {
|
||||||
|
updatedProperties.getProperties()?.let { putAll(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check with a screen event
|
||||||
|
val fakeScreen = aVectorAnalyticsScreen("Screen", mutableMapOf("foo" to "bar"))
|
||||||
|
defaultVectorAnalytics.screen(fakeScreen)
|
||||||
|
|
||||||
|
fakePostHog.verifyScreenTracked(
|
||||||
|
"Screen",
|
||||||
|
fakeScreen.getProperties().clearNulls()?.toMutableMap()?.apply {
|
||||||
|
updatedProperties.getProperties()?.let { putAll(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Super properties can be updated`() = runTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||||
|
|
||||||
|
val superProperties = SuperProperties(
|
||||||
|
platformCodeName = SuperProperties.PlatformCodeName.EA,
|
||||||
|
cryptoSDKVersion = "0.0",
|
||||||
|
cryptoSDK = SuperProperties.CryptoSDK.Rust
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.updateSuperProperties(superProperties)
|
||||||
|
|
||||||
|
val fakeEvent = aVectorAnalyticsEvent("THE_NAME", mutableMapOf("foo" to "bar"))
|
||||||
|
defaultVectorAnalytics.capture(fakeEvent)
|
||||||
|
|
||||||
|
fakePostHog.verifyEventTracked(
|
||||||
|
"THE_NAME",
|
||||||
|
fakeEvent.getProperties().clearNulls()?.toMutableMap()?.apply {
|
||||||
|
superProperties.getProperties()?.let { putAll(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val superPropertiesUpdate = superProperties.copy(cryptoSDKVersion = "1.0")
|
||||||
|
defaultVectorAnalytics.updateSuperProperties(superPropertiesUpdate)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.capture(fakeEvent)
|
||||||
|
|
||||||
|
fakePostHog.verifyEventTracked(
|
||||||
|
"THE_NAME",
|
||||||
|
fakeEvent.getProperties().clearNulls()?.toMutableMap()?.apply {
|
||||||
|
superPropertiesUpdate.getProperties()?.let { putAll(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Super properties should not override event property`() = runTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||||
|
|
||||||
|
val superProperties = SuperProperties(
|
||||||
|
cryptoSDKVersion = "0.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.updateSuperProperties(superProperties)
|
||||||
|
|
||||||
|
val fakeEvent = aVectorAnalyticsEvent("THE_NAME", mutableMapOf("cryptoSDKVersion" to "XXX"))
|
||||||
|
defaultVectorAnalytics.capture(fakeEvent)
|
||||||
|
|
||||||
|
fakePostHog.verifyEventTracked(
|
||||||
|
"THE_NAME",
|
||||||
|
mapOf(
|
||||||
|
"cryptoSDKVersion" to "XXX"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Super properties should be added to event with no properties`() = runTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||||
|
|
||||||
|
val superProperties = SuperProperties(
|
||||||
|
cryptoSDKVersion = "0.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.updateSuperProperties(superProperties)
|
||||||
|
|
||||||
|
val fakeEvent = aVectorAnalyticsEvent("THE_NAME", null)
|
||||||
|
defaultVectorAnalytics.capture(fakeEvent)
|
||||||
|
|
||||||
|
fakePostHog.verifyEventTracked(
|
||||||
|
"THE_NAME",
|
||||||
|
mapOf(
|
||||||
|
"cryptoSDKVersion" to "0.0"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun Map<String, Any?>?.clearNulls(): Map<String, Any>? {
|
private fun Map<String, Any?>?.clearNulls(): Map<String, Any>? {
|
||||||
if (this == null) return null
|
if (this == null) return null
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue