Merge pull request #8820 from element-hq/feature/bca/bump_posthog_version_3
Bump posthog version to 3.2.0
This commit is contained in:
commit
4acbe4e582
|
@ -0,0 +1 @@
|
||||||
|
Update posthog sdk to 3.2.0
|
|
@ -121,7 +121,7 @@ ext.groups = [
|
||||||
'com.parse.bolts',
|
'com.parse.bolts',
|
||||||
'com.pinterest',
|
'com.pinterest',
|
||||||
'com.pinterest.ktlint',
|
'com.pinterest.ktlint',
|
||||||
'com.posthog.android',
|
'com.posthog',
|
||||||
'com.squareup',
|
'com.squareup',
|
||||||
'com.squareup.curtains',
|
'com.squareup.curtains',
|
||||||
'com.squareup.duktape',
|
'com.squareup.duktape',
|
||||||
|
|
|
@ -234,9 +234,7 @@ dependencies {
|
||||||
kapt libs.dagger.hiltCompiler
|
kapt libs.dagger.hiltCompiler
|
||||||
|
|
||||||
// Analytics
|
// Analytics
|
||||||
implementation('com.posthog.android:posthog:2.0.3') {
|
implementation 'com.posthog:posthog-android:3.2.0'
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
|
||||||
}
|
|
||||||
implementation libs.sentry.sentryAndroid
|
implementation libs.sentry.sentryAndroid
|
||||||
|
|
||||||
// UnifiedPush
|
// UnifiedPush
|
||||||
|
|
|
@ -16,9 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
import com.posthog.android.Options
|
import com.posthog.PostHogInterface
|
||||||
import com.posthog.android.PostHog
|
|
||||||
import com.posthog.android.Properties
|
|
||||||
import im.vector.app.core.di.NamedGlobalScope
|
import im.vector.app.core.di.NamedGlobalScope
|
||||||
import im.vector.app.features.analytics.AnalyticsConfig
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
import im.vector.app.features.analytics.VectorAnalytics
|
import im.vector.app.features.analytics.VectorAnalytics
|
||||||
|
@ -36,9 +34,6 @@ import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
private val REUSE_EXISTING_ID: String? = null
|
|
||||||
private val IGNORED_OPTIONS: Options? = null
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class DefaultVectorAnalytics @Inject constructor(
|
class DefaultVectorAnalytics @Inject constructor(
|
||||||
private val postHogFactory: PostHogFactory,
|
private val postHogFactory: PostHogFactory,
|
||||||
|
@ -49,9 +44,9 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
@NamedGlobalScope private val globalScope: CoroutineScope
|
@NamedGlobalScope private val globalScope: CoroutineScope
|
||||||
) : VectorAnalytics {
|
) : VectorAnalytics {
|
||||||
|
|
||||||
private var posthog: PostHog? = null
|
private var posthog: PostHogInterface? = null
|
||||||
|
|
||||||
private fun createPosthog(): PostHog? {
|
private fun createPosthog(): PostHogInterface? {
|
||||||
return when {
|
return when {
|
||||||
analyticsConfig.isEnabled -> postHogFactory.createPosthog()
|
analyticsConfig.isEnabled -> postHogFactory.createPosthog()
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -126,7 +121,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
posthog?.reset()
|
posthog?.reset()
|
||||||
} else {
|
} else {
|
||||||
Timber.tag(analyticsTag.value).d("identify")
|
Timber.tag(analyticsTag.value).d("identify")
|
||||||
posthog?.identify(id, lateInitUserPropertiesFactory.createUserProperties()?.getProperties()?.toPostHogUserProperties(), IGNORED_OPTIONS)
|
posthog?.identify(id, lateInitUserPropertiesFactory.createUserProperties()?.getProperties()?.toPostHogUserProperties())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +150,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
when (_userConsent) {
|
when (_userConsent) {
|
||||||
true -> {
|
true -> {
|
||||||
posthog = createPosthog()
|
posthog = createPosthog()
|
||||||
posthog?.optOut(false)
|
posthog?.optIn()
|
||||||
identifyPostHog()
|
identifyPostHog()
|
||||||
pendingUserProperties?.let { doUpdateUserProperties(it) }
|
pendingUserProperties?.let { doUpdateUserProperties(it) }
|
||||||
pendingUserProperties = null
|
pendingUserProperties = null
|
||||||
|
@ -163,8 +158,8 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
false -> {
|
false -> {
|
||||||
// When opting out, ensure that the queue is flushed first, or it will be flushed later (after user has revoked consent)
|
// When opting out, ensure that the queue is flushed first, or it will be flushed later (after user has revoked consent)
|
||||||
posthog?.flush()
|
posthog?.flush()
|
||||||
posthog?.optOut(true)
|
posthog?.optOut()
|
||||||
posthog?.shutdown()
|
posthog?.close()
|
||||||
posthog = null
|
posthog = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,6 +172,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
?.takeIf { userConsent == true }
|
?.takeIf { userConsent == true }
|
||||||
?.capture(
|
?.capture(
|
||||||
event.getName(),
|
event.getName(),
|
||||||
|
analyticsId,
|
||||||
event.getProperties()?.toPostHogProperties()
|
event.getProperties()?.toPostHogProperties()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -197,27 +193,37 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doUpdateUserProperties(userProperties: UserProperties) {
|
private fun doUpdateUserProperties(userProperties: UserProperties) {
|
||||||
|
// we need a distinct id to set user properties
|
||||||
|
val distinctId = analyticsId ?: return
|
||||||
posthog
|
posthog
|
||||||
?.takeIf { userConsent == true }
|
?.takeIf { userConsent == true }
|
||||||
?.identify(REUSE_EXISTING_ID, userProperties.getProperties()?.toPostHogUserProperties(), IGNORED_OPTIONS)
|
?.identify(distinctId, userProperties.getProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Map<String, Any?>?.toPostHogProperties(): Properties? {
|
private fun Map<String, Any?>?.toPostHogProperties(): Map<String, Any>? {
|
||||||
if (this == null) return null
|
if (this == null) return null
|
||||||
|
|
||||||
return Properties().apply {
|
val nonNulls = HashMap<String, Any>()
|
||||||
putAll(this@toPostHogProperties)
|
this.forEach { (key, value) ->
|
||||||
|
if (value != null) {
|
||||||
|
nonNulls[key] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nonNulls
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We avoid sending nulls as part of the UserProperties as this will reset the values across all devices.
|
* We avoid sending nulls as part of the UserProperties as this will reset the values across all devices.
|
||||||
* The UserProperties event has nullable properties to allow for clients to opt in.
|
* The UserProperties event has nullable properties to allow for clients to opt in.
|
||||||
*/
|
*/
|
||||||
private fun Map<String, Any?>.toPostHogUserProperties(): Properties {
|
private fun Map<String, Any?>.toPostHogUserProperties(): Map<String, Any> {
|
||||||
return Properties().apply {
|
val nonNulls = HashMap<String, Any>()
|
||||||
putAll(this@toPostHogUserProperties.filter { it.value != null })
|
this.forEach { (key, value) ->
|
||||||
|
if (value != null) {
|
||||||
|
nonNulls[key] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nonNulls
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun trackError(throwable: Throwable) {
|
override fun trackError(throwable: Throwable) {
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.posthog.android.PostHog
|
import com.posthog.PostHogInterface
|
||||||
|
import com.posthog.android.PostHogAndroid
|
||||||
|
import com.posthog.android.PostHogAndroidConfig
|
||||||
import im.vector.app.core.resources.BuildMeta
|
import im.vector.app.core.resources.BuildMeta
|
||||||
import im.vector.app.features.analytics.AnalyticsConfig
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -28,29 +30,17 @@ class PostHogFactory @Inject constructor(
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun createPosthog(): PostHog {
|
fun createPosthog(): PostHogInterface {
|
||||||
return PostHog.Builder(context, analyticsConfig.postHogApiKey, analyticsConfig.postHogHost)
|
val config = PostHogAndroidConfig(
|
||||||
// Record certain application events automatically! (off/false by default)
|
apiKey = analyticsConfig.postHogApiKey,
|
||||||
// .captureApplicationLifecycleEvents()
|
host = analyticsConfig.postHogHost,
|
||||||
// Record screen views automatically! (off/false by default)
|
// we do that manually
|
||||||
// .recordScreenViews()
|
captureScreenViews = false,
|
||||||
// Capture deep links as part of the screen call. (off by default)
|
).also {
|
||||||
// .captureDeepLinks()
|
if (buildMeta.isDebug) {
|
||||||
// Maximum number of events to keep in queue before flushing (default 20)
|
it.debug = true
|
||||||
// .flushQueueSize(20)
|
}
|
||||||
// Max delay before flushing the queue (30 seconds)
|
|
||||||
// .flushInterval(30, TimeUnit.SECONDS)
|
|
||||||
// Enable or disable collection of ANDROID_ID (true)
|
|
||||||
.collectDeviceId(false)
|
|
||||||
.logLevel(getLogLevel())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLogLevel(): PostHog.LogLevel {
|
|
||||||
return if (buildMeta.isDebug) {
|
|
||||||
PostHog.LogLevel.DEBUG
|
|
||||||
} else {
|
|
||||||
PostHog.LogLevel.INFO
|
|
||||||
}
|
}
|
||||||
|
return PostHogAndroid.with(context, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
import com.posthog.android.Properties
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
|
||||||
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
|
||||||
|
@ -128,7 +125,7 @@ class DefaultVectorAnalyticsTest {
|
||||||
|
|
||||||
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
||||||
|
|
||||||
fakePostHog.verifyScreenTracked(A_SCREEN_EVENT.getName(), A_SCREEN_EVENT.toPostHogProperties())
|
fakePostHog.verifyScreenTracked(A_SCREEN_EVENT.getName(), A_SCREEN_EVENT.getProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -146,7 +143,7 @@ class DefaultVectorAnalyticsTest {
|
||||||
|
|
||||||
defaultVectorAnalytics.capture(AN_EVENT)
|
defaultVectorAnalytics.capture(AN_EVENT)
|
||||||
|
|
||||||
fakePostHog.verifyEventTracked(AN_EVENT.getName(), AN_EVENT.toPostHogProperties())
|
fakePostHog.verifyEventTracked(AN_EVENT.getName(), AN_EVENT.getProperties().clearNulls())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -176,16 +173,16 @@ class DefaultVectorAnalyticsTest {
|
||||||
|
|
||||||
fakeSentryAnalytics.verifyNoErrorTracking()
|
fakeSentryAnalytics.verifyNoErrorTracking()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun VectorAnalyticsScreen.toPostHogProperties(): Properties? {
|
private fun Map<String, Any?>?.clearNulls(): Map<String, Any>? {
|
||||||
return getProperties()?.let { properties ->
|
if (this == null) return null
|
||||||
Properties().also { it.putAll(properties) }
|
|
||||||
}
|
val nonNulls = HashMap<String, Any>()
|
||||||
}
|
this.forEach { (key, value) ->
|
||||||
|
if (value != null) {
|
||||||
private fun VectorAnalyticsEvent.toPostHogProperties(): Properties? {
|
nonNulls[key] = value
|
||||||
return getProperties()?.let { properties ->
|
}
|
||||||
Properties().also { it.putAll(properties) }
|
}
|
||||||
|
return nonNulls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import com.posthog.android.PostHog
|
import com.posthog.PostHogInterface
|
||||||
import com.posthog.android.Properties
|
|
||||||
import im.vector.app.features.analytics.plan.UserProperties
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -36,16 +35,19 @@ class FakePostHog {
|
||||||
every { Looper.getMainLooper() } returns looper
|
every { Looper.getMainLooper() } returns looper
|
||||||
}
|
}
|
||||||
|
|
||||||
val instance = mockk<PostHog>(relaxed = true)
|
val instance = mockk<PostHogInterface>(relaxed = true)
|
||||||
|
|
||||||
fun verifyOptOutStatus(optedOut: Boolean) {
|
fun verifyOptOutStatus(optedOut: Boolean) {
|
||||||
verify { instance.optOut(optedOut) }
|
if (optedOut) {
|
||||||
|
verify { instance.optOut() }
|
||||||
|
} else {
|
||||||
|
verify { instance.optIn() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyIdentifies(analyticsId: String, userProperties: UserProperties?) {
|
fun verifyIdentifies(analyticsId: String, userProperties: UserProperties?) {
|
||||||
verify {
|
verify {
|
||||||
val postHogProperties = userProperties?.getProperties()
|
val postHogProperties = userProperties?.getProperties()
|
||||||
?.let { rawProperties -> Properties().also { it.putAll(rawProperties) } }
|
|
||||||
?.takeIf { it.isNotEmpty() }
|
?.takeIf { it.isNotEmpty() }
|
||||||
instance.identify(analyticsId, postHogProperties, null)
|
instance.identify(analyticsId, postHogProperties, null)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +57,7 @@ class FakePostHog {
|
||||||
verify { instance.reset() }
|
verify { instance.reset() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyScreenTracked(name: String, properties: Properties?) {
|
fun verifyScreenTracked(name: String, properties: Map<String, Any>?) {
|
||||||
verify { instance.screen(name, properties) }
|
verify { instance.screen(name, properties) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,12 +65,11 @@ class FakePostHog {
|
||||||
verify(exactly = 0) {
|
verify(exactly = 0) {
|
||||||
instance.screen(any())
|
instance.screen(any())
|
||||||
instance.screen(any(), any())
|
instance.screen(any(), any())
|
||||||
instance.screen(any(), any(), any())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyEventTracked(name: String, properties: Properties?) {
|
fun verifyEventTracked(name: String, properties: Map<String, Any>?) {
|
||||||
verify { instance.capture(name, properties) }
|
verify { instance.capture(name, null, properties) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyNoEventTracking() {
|
fun verifyNoEventTracking() {
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import com.posthog.android.PostHog
|
import com.posthog.PostHogInterface
|
||||||
import im.vector.app.features.analytics.impl.PostHogFactory
|
import im.vector.app.features.analytics.impl.PostHogFactory
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
|
||||||
class FakePostHogFactory(postHog: PostHog) {
|
class FakePostHogFactory(postHog: PostHogInterface) {
|
||||||
val instance = mockk<PostHogFactory>().also {
|
val instance = mockk<PostHogFactory>().also {
|
||||||
every { it.createPosthog() } returns postHog
|
every { it.createPosthog() } returns postHog
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue