From 1686243283da0fba2a6c281e41525c9ed83dd388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Tue, 17 Jun 2025 12:48:25 +0200 Subject: [PATCH 01/12] Create Analytics module with a first version of TracksTracker The first version of the Tracker is being injected in the app module, more concretely in the test viewmodel to check that everything works as expected. --- analytics/.gitignore | 1 + analytics/build.gradle.kts | 40 +++++++++++++++++++ analytics/consumer-rules.pro | 0 analytics/proguard-rules.pro | 21 ++++++++++ analytics/src/main/AndroidManifest.xml | 4 ++ .../kotlin/com/gravatar/analytics/Event.kt | 5 +++ .../kotlin/com/gravatar/analytics/Tracker.kt | 5 +++ .../gravatar/analytics/di/AnalyticsModule.kt | 11 +++++ .../analytics/tracks/TracksTracker.kt | 17 ++++++++ .../analytics/di/AnalyticsModuleTest.kt | 23 +++++++++++ app/build.gradle.kts | 2 + app/src/main/AndroidManifest.xml | 2 + .../com/gravatar/app/GravatarApplication.kt | 3 +- .../java/com/gravatar/app/MainActivity.kt | 8 ++++ .../com/gravatar/app/analytics/TestEvent.kt | 7 ++++ .../com/gravatar/app/di/AppModuleTest.kt | 9 ++++- gradle/libs.versions.toml | 13 +++--- settings.gradle.kts | 8 ++++ 18 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 analytics/.gitignore create mode 100644 analytics/build.gradle.kts create mode 100644 analytics/consumer-rules.pro create mode 100644 analytics/proguard-rules.pro create mode 100644 analytics/src/main/AndroidManifest.xml create mode 100644 analytics/src/main/kotlin/com/gravatar/analytics/Event.kt create mode 100644 analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt create mode 100644 analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt create mode 100644 analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt create mode 100644 analytics/src/test/java/com/gravatar/analytics/di/AnalyticsModuleTest.kt create mode 100644 app/src/main/java/com/gravatar/app/analytics/TestEvent.kt diff --git a/analytics/.gitignore b/analytics/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/analytics/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/analytics/build.gradle.kts b/analytics/build.gradle.kts new file mode 100644 index 00000000..718e4cd1 --- /dev/null +++ b/analytics/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.gravatar.analytics" + compileSdk = 35 + + defaultConfig { + minSdk = 24 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } +} + +dependencies { + + implementation(project.dependencies.platform(libs.koin.bom)) + implementation(libs.koin.core) + implementation(libs.automattic.tracks) + + testImplementation(libs.junit) + testImplementation(libs.koin.test.junit4) +} \ No newline at end of file diff --git a/analytics/consumer-rules.pro b/analytics/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/analytics/proguard-rules.pro b/analytics/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/analytics/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/analytics/src/main/AndroidManifest.xml b/analytics/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/analytics/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/Event.kt b/analytics/src/main/kotlin/com/gravatar/analytics/Event.kt new file mode 100644 index 00000000..29b7326e --- /dev/null +++ b/analytics/src/main/kotlin/com/gravatar/analytics/Event.kt @@ -0,0 +1,5 @@ +package com.gravatar.analytics + +interface Event { + val name: String +} \ No newline at end of file diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt new file mode 100644 index 00000000..462f0570 --- /dev/null +++ b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt @@ -0,0 +1,5 @@ +package com.gravatar.analytics + +interface Tracker { + fun trackEvent(event: Event) +} \ No newline at end of file diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt b/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt new file mode 100644 index 00000000..51390c95 --- /dev/null +++ b/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt @@ -0,0 +1,11 @@ +package com.gravatar.analytics.di + +import com.gravatar.analytics.Tracker +import com.gravatar.analytics.tracks.TracksTracker +import org.koin.core.module.dsl.bind +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val analyticsModule = module { + singleOf(::TracksTracker) { bind() } +} \ No newline at end of file diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt new file mode 100644 index 00000000..6a1289e6 --- /dev/null +++ b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt @@ -0,0 +1,17 @@ +package com.gravatar.analytics.tracks + +import android.content.Context +import com.automattic.android.tracks.TracksClient +import com.gravatar.analytics.Event +import com.gravatar.analytics.Tracker + +internal class TracksTracker(context: Context) : Tracker { + + private val tracksClient = TracksClient.getClient(context) + + override fun trackEvent(event: Event) { + // TODO: We should add the userId to the event if available and set the user type accordingly. + tracksClient.track(event.name, null, TracksClient.NosaraUserType.ANON) + tracksClient.flush() + } +} \ No newline at end of file diff --git a/analytics/src/test/java/com/gravatar/analytics/di/AnalyticsModuleTest.kt b/analytics/src/test/java/com/gravatar/analytics/di/AnalyticsModuleTest.kt new file mode 100644 index 00000000..f4090449 --- /dev/null +++ b/analytics/src/test/java/com/gravatar/analytics/di/AnalyticsModuleTest.kt @@ -0,0 +1,23 @@ +package com.gravatar.analytics.di + +import android.content.Context +import com.gravatar.analytics.tracks.TracksTracker +import org.junit.Test +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.test.KoinTest +import org.koin.test.verify.definition +import org.koin.test.verify.injectedParameters +import org.koin.test.verify.verify + +class AnalyticsModuleTest : KoinTest { + + @OptIn(KoinExperimentalAPI::class) + @Test + fun checkAllModules() { + analyticsModule.verify( + injections = injectedParameters( + definition(Context::class) + ) + ) + } +} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2f280611..7703c41b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,6 +10,8 @@ android { dependencies { implementation(project(":homeUi")) implementation(project(":loginUi")) + implementation(project(":analytics")) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae3b5445..b9e6b8c9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + (Tracker::class) + ) + ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 546228d6..3e21e4cf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,15 @@ [versions] androidGradlePlugin = "8.10.1" activityCompose = "1.10.1" -kotlin = "2.1.21" -coreKtx = "1.16.0" composeBom = "2025.06.00" +coreKtx = "1.16.0" detekt = "1.23.8" junit = "4.13.2" koin-bom = "4.1.0" +kotlin = "2.1.21" lifecycleRuntimeKtx = "2.9.1" navigationCompose = "2.9.0" +tracks = "6.0.3" [libraries] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } @@ -16,16 +17,18 @@ androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", versi androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +# Tracks - Analytics and Crash Reporting +automattic-tracks = { module = "com.automattic:Automattic-Tracks-Android", version.ref = "tracks" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } -detekt-ktlint-wrapper = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt"} +detekt-ktlint-wrapper = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } junit = { group = "junit", name = "junit", version.ref = "junit" } koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" } -koin-core = { module = "io.insert-koin:koin-core"} +koin-core = { module = "io.insert-koin:koin-core" } koin-android = { module = "io.insert-koin:koin-android" } koin-test-junit4 = { module = "io.insert-koin:koin-test-junit4" } @@ -37,11 +40,11 @@ detekt-gradlePlugin = { group = "io.gitlab.arturbosch.detekt", name = "io.gitlab [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } -android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } # Plugins defined by this project gravatar-android-application = { id = "gravatar.android.application" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 637dc59f..427bd3e1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,13 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven { + setUrl("https://a8c-libs.s3.amazonaws.com/android") + content { + includeGroup("com.automattic") + includeGroup("com.automattic.tracks") + } + } } } @@ -24,3 +31,4 @@ rootProject.name = "Gravatar" include(":app") include(":homeUi") include(":loginUi") +include(":analytics") From eee9c8161315fa5a3b0ffbf0da239abee0a84255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Tue, 17 Jun 2025 12:54:14 +0200 Subject: [PATCH 02/12] Update gitignore to properly omit build folders --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e16803b4..15e7df3b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,6 @@ local.properties *.iml # generated files -/build +build/ /captures .kotlin/ From 832b1dea9c66c58360ff9ae9c47ba6def0585ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Tue, 17 Jun 2025 16:30:25 +0200 Subject: [PATCH 03/12] Introduce GravatarAndroidLibraryConventionPlugin --- analytics/build.gradle.kts | 25 +------------------ .../kotlin/com/gravatar/analytics/Event.kt | 2 +- .../kotlin/com/gravatar/analytics/Tracker.kt | 2 +- .../gravatar/analytics/di/AnalyticsModule.kt | 2 +- .../analytics/tracks/TracksTracker.kt | 4 +-- 5 files changed, 6 insertions(+), 29 deletions(-) diff --git a/analytics/build.gradle.kts b/analytics/build.gradle.kts index 718e4cd1..eea33d84 100644 --- a/analytics/build.gradle.kts +++ b/analytics/build.gradle.kts @@ -1,32 +1,9 @@ plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) + alias(libs.plugins.gravatar.android.library) } android { namespace = "com.gravatar.analytics" - compileSdk = 35 - - defaultConfig { - minSdk = 24 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = "11" - } } dependencies { diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/Event.kt b/analytics/src/main/kotlin/com/gravatar/analytics/Event.kt index 29b7326e..a0905d98 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/Event.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/Event.kt @@ -2,4 +2,4 @@ package com.gravatar.analytics interface Event { val name: String -} \ No newline at end of file +} diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt index 462f0570..8337f818 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt @@ -2,4 +2,4 @@ package com.gravatar.analytics interface Tracker { fun trackEvent(event: Event) -} \ No newline at end of file +} diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt b/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt index 51390c95..f414d7c0 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt @@ -8,4 +8,4 @@ import org.koin.dsl.module val analyticsModule = module { singleOf(::TracksTracker) { bind() } -} \ No newline at end of file +} diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt index 6a1289e6..eb21e03a 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt @@ -10,8 +10,8 @@ internal class TracksTracker(context: Context) : Tracker { private val tracksClient = TracksClient.getClient(context) override fun trackEvent(event: Event) { - // TODO: We should add the userId to the event if available and set the user type accordingly. + // We should add the userId to the event if available and set the user type accordingly. tracksClient.track(event.name, null, TracksClient.NosaraUserType.ANON) tracksClient.flush() } -} \ No newline at end of file +} From d7ff5c5a6cfd6e6ea60de2403f68e3db43301db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Tue, 17 Jun 2025 17:15:21 +0200 Subject: [PATCH 04/12] Declare AppEvent as a sealed class --- .../src/main/kotlin/com/gravatar/analytics/Tracker.kt | 1 + .../com/gravatar/analytics/tracks/TracksTracker.kt | 5 ++++- app/src/main/java/com/gravatar/app/MainActivity.kt | 4 ++-- .../main/java/com/gravatar/app/analytics/AppEvent.kt | 10 ++++++++++ .../main/java/com/gravatar/app/analytics/TestEvent.kt | 7 ------- .../test/kotlin/com/gravatar/app/di/AppModuleTest.kt | 9 +-------- 6 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/gravatar/app/analytics/AppEvent.kt delete mode 100644 app/src/main/java/com/gravatar/app/analytics/TestEvent.kt diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt index 8337f818..c39d719d 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt @@ -2,4 +2,5 @@ package com.gravatar.analytics interface Tracker { fun trackEvent(event: Event) + fun flush() } diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt index eb21e03a..e0ca0c37 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt @@ -11,7 +11,10 @@ internal class TracksTracker(context: Context) : Tracker { override fun trackEvent(event: Event) { // We should add the userId to the event if available and set the user type accordingly. - tracksClient.track(event.name, null, TracksClient.NosaraUserType.ANON) + tracksClient.track(event.name, "Gravatar", TracksClient.NosaraUserType.ANON) + } + + override fun flush() { tracksClient.flush() } } diff --git a/app/src/main/java/com/gravatar/app/MainActivity.kt b/app/src/main/java/com/gravatar/app/MainActivity.kt index 7ebe6377..ef0fc3ca 100644 --- a/app/src/main/java/com/gravatar/app/MainActivity.kt +++ b/app/src/main/java/com/gravatar/app/MainActivity.kt @@ -5,7 +5,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import com.gravatar.analytics.Tracker -import com.gravatar.app.analytics.TestEvent +import com.gravatar.app.analytics.AppEvent import com.gravatar.app.navigation.RootNavigation import com.gravatar.app.ui.theme.GravatarTheme import org.koin.android.ext.android.inject @@ -18,7 +18,7 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() - tracker.trackEvent(TestEvent()) + tracker.trackEvent(AppEvent.Test) setContent { GravatarTheme { diff --git a/app/src/main/java/com/gravatar/app/analytics/AppEvent.kt b/app/src/main/java/com/gravatar/app/analytics/AppEvent.kt new file mode 100644 index 00000000..e6ac8bf8 --- /dev/null +++ b/app/src/main/java/com/gravatar/app/analytics/AppEvent.kt @@ -0,0 +1,10 @@ +package com.gravatar.app.analytics + +import com.gravatar.analytics.Event + +sealed class AppEvent : Event { + + data object Test : AppEvent() { + override val name: String = "gravatar_android_test" + } +} diff --git a/app/src/main/java/com/gravatar/app/analytics/TestEvent.kt b/app/src/main/java/com/gravatar/app/analytics/TestEvent.kt deleted file mode 100644 index 43dc2797..00000000 --- a/app/src/main/java/com/gravatar/app/analytics/TestEvent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.gravatar.app.analytics - -import com.gravatar.analytics.Event - -class TestEvent : Event { - override val name: String = "gravatar_android_test" -} diff --git a/app/src/test/kotlin/com/gravatar/app/di/AppModuleTest.kt b/app/src/test/kotlin/com/gravatar/app/di/AppModuleTest.kt index c072bae0..6528b067 100644 --- a/app/src/test/kotlin/com/gravatar/app/di/AppModuleTest.kt +++ b/app/src/test/kotlin/com/gravatar/app/di/AppModuleTest.kt @@ -1,11 +1,8 @@ package com.gravatar.app.di -import com.gravatar.analytics.Tracker import org.junit.Test import org.koin.core.annotation.KoinExperimentalAPI import org.koin.test.KoinTest -import org.koin.test.verify.definition -import org.koin.test.verify.injectedParameters import org.koin.test.verify.verify class AppModuleTest : KoinTest { @@ -13,10 +10,6 @@ class AppModuleTest : KoinTest { @OptIn(KoinExperimentalAPI::class) @Test fun checkAllModules() { - appModule.verify( - injections = injectedParameters( - definition(Tracker::class) - ) - ) + appModule.verify() } } From 16d0f16b1b2022a4dd8f658fc47970c8157703ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Tue, 17 Jun 2025 17:31:18 +0200 Subject: [PATCH 05/12] Injecting TracksClient into TracksTracker --- .../main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt | 2 ++ .../kotlin/com/gravatar/analytics/tracks/TracksTracker.kt | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt b/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt index f414d7c0..46439b8a 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/di/AnalyticsModule.kt @@ -1,5 +1,6 @@ package com.gravatar.analytics.di +import com.automattic.android.tracks.TracksClient import com.gravatar.analytics.Tracker import com.gravatar.analytics.tracks.TracksTracker import org.koin.core.module.dsl.bind @@ -7,5 +8,6 @@ import org.koin.core.module.dsl.singleOf import org.koin.dsl.module val analyticsModule = module { + single { TracksClient.getClient(get()) } singleOf(::TracksTracker) { bind() } } diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt index e0ca0c37..9cd74937 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt @@ -1,13 +1,10 @@ package com.gravatar.analytics.tracks -import android.content.Context import com.automattic.android.tracks.TracksClient import com.gravatar.analytics.Event import com.gravatar.analytics.Tracker -internal class TracksTracker(context: Context) : Tracker { - - private val tracksClient = TracksClient.getClient(context) +internal class TracksTracker(private val tracksClient: TracksClient) : Tracker { override fun trackEvent(event: Event) { // We should add the userId to the event if available and set the user type accordingly. From 4b085a4b8818cd4331bb55f2de85619a9dcef89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Tue, 17 Jun 2025 17:43:45 +0200 Subject: [PATCH 06/12] Add "userId" as Tracker parameter --- .../main/kotlin/com/gravatar/analytics/Tracker.kt | 7 ++++--- .../com/gravatar/analytics/tracks/TracksTracker.kt | 14 +++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt index c39d719d..fc9394cc 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt @@ -1,6 +1,7 @@ package com.gravatar.analytics -interface Tracker { - fun trackEvent(event: Event) - fun flush() +abstract class Tracker { + abstract var userId: String? + abstract fun trackEvent(event: Event) + abstract fun flush() } diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt index 9cd74937..1e2981e2 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt @@ -3,15 +3,23 @@ package com.gravatar.analytics.tracks import com.automattic.android.tracks.TracksClient import com.gravatar.analytics.Event import com.gravatar.analytics.Tracker +import java.util.UUID -internal class TracksTracker(private val tracksClient: TracksClient) : Tracker { +internal class TracksTracker(private val tracksClient: TracksClient) : Tracker() { + override var userId: String? = null + private val anonId: String = generateNewAnonID() override fun trackEvent(event: Event) { - // We should add the userId to the event if available and set the user type accordingly. - tracksClient.track(event.name, "Gravatar", TracksClient.NosaraUserType.ANON) + // We should add GRAVATAR userType when available. + tracksClient.track(event.name, userId ?: anonId, TracksClient.NosaraUserType.ANON) } override fun flush() { tracksClient.flush() } } + +private fun generateNewAnonID(): String { + // Generate a new UUID and return it as a string. + return UUID.randomUUID().toString() +} From 66fe71e23cd2ce321290191ef512304732275390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Tue, 17 Jun 2025 17:55:50 +0200 Subject: [PATCH 07/12] Added some unit tests to TrackTracker --- analytics/build.gradle.kts | 1 + .../analytics/di/AnalyticsModuleTest.kt | 0 .../analytics/tracks/TrackTrackerTest.kt | 34 +++++++++++++++++++ gradle/libs.versions.toml | 2 ++ 4 files changed, 37 insertions(+) rename analytics/src/test/{java => kotlin}/com/gravatar/analytics/di/AnalyticsModuleTest.kt (100%) create mode 100644 analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt diff --git a/analytics/build.gradle.kts b/analytics/build.gradle.kts index eea33d84..0950dec3 100644 --- a/analytics/build.gradle.kts +++ b/analytics/build.gradle.kts @@ -14,4 +14,5 @@ dependencies { testImplementation(libs.junit) testImplementation(libs.koin.test.junit4) + testImplementation(libs.mockk.android) } \ No newline at end of file diff --git a/analytics/src/test/java/com/gravatar/analytics/di/AnalyticsModuleTest.kt b/analytics/src/test/kotlin/com/gravatar/analytics/di/AnalyticsModuleTest.kt similarity index 100% rename from analytics/src/test/java/com/gravatar/analytics/di/AnalyticsModuleTest.kt rename to analytics/src/test/kotlin/com/gravatar/analytics/di/AnalyticsModuleTest.kt diff --git a/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt new file mode 100644 index 00000000..7dd9947e --- /dev/null +++ b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt @@ -0,0 +1,34 @@ +package com.gravatar.analytics.tracks + +import com.automattic.android.tracks.TracksClient +import com.gravatar.analytics.Event +import io.mockk.mockk +import io.mockk.verify +import org.junit.Test + +class TrackTrackerTest { + + @Test + fun `when trackEvent is invoked then call track on client`() { + val mockClient = mockk(relaxed = true) + val tracker = TracksTracker(mockClient) + tracker.userId = "test_user_id" + val event = object : Event { + override val name: String = "test_event" + } + + tracker.trackEvent(event) + + verify { mockClient.track(event.name, "test_user_id", TracksClient.NosaraUserType.ANON) } + } + + @Test + fun `when flush is invoked then call flush on client`() { + val mockClient = mockk(relaxed = true) + val tracker = TracksTracker(mockClient) + + tracker.flush() + + verify(exactly = 1) { mockClient.flush() } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3e21e4cf..94090742 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ koin-bom = "4.1.0" kotlin = "2.1.21" lifecycleRuntimeKtx = "2.9.1" navigationCompose = "2.9.0" +mockk = "1.14.2" tracks = "6.0.3" [libraries] @@ -31,6 +32,7 @@ koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" } koin-core = { module = "io.insert-koin:koin-core" } koin-android = { module = "io.insert-koin:koin-android" } koin-test-junit4 = { module = "io.insert-koin:koin-test-junit4" } +mockk-android = { group = "io.mockk", name = "mockk-android", version.ref = "mockk" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } From 35ae904e415f6c2c9cb6be2f644c51296db95f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Wed, 18 Jun 2025 11:31:22 +0200 Subject: [PATCH 08/12] Remove unnecessary manifest --- analytics/src/main/AndroidManifest.xml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 analytics/src/main/AndroidManifest.xml diff --git a/analytics/src/main/AndroidManifest.xml b/analytics/src/main/AndroidManifest.xml deleted file mode 100644 index a5918e68..00000000 --- a/analytics/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From b4e3303f47ea5971988d6a7cd4607fdf4eba7a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Wed, 18 Jun 2025 11:32:50 +0200 Subject: [PATCH 09/12] Extract common code in test to a @Before method --- .../gravatar/analytics/tracks/TrackTrackerTest.kt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt index 7dd9947e..703719df 100644 --- a/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt +++ b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt @@ -4,14 +4,22 @@ import com.automattic.android.tracks.TracksClient import com.gravatar.analytics.Event import io.mockk.mockk import io.mockk.verify +import org.junit.Before import org.junit.Test class TrackTrackerTest { + private lateinit var tracker: TracksTracker + private lateinit var mockClient: TracksClient + + @Before + fun setUp() { + mockClient = mockk(relaxed = true) + tracker = TracksTracker(mockClient) + } + @Test fun `when trackEvent is invoked then call track on client`() { - val mockClient = mockk(relaxed = true) - val tracker = TracksTracker(mockClient) tracker.userId = "test_user_id" val event = object : Event { override val name: String = "test_event" @@ -24,9 +32,6 @@ class TrackTrackerTest { @Test fun `when flush is invoked then call flush on client`() { - val mockClient = mockk(relaxed = true) - val tracker = TracksTracker(mockClient) - tracker.flush() verify(exactly = 1) { mockClient.flush() } From 93412be623ffcc3aa1f8862655aa4136d14a385c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Wed, 18 Jun 2025 13:39:17 +0200 Subject: [PATCH 10/12] Include analytics module inside AppModule instead load it independently --- app/src/main/java/com/gravatar/app/GravatarApplication.kt | 3 +-- app/src/main/java/com/gravatar/app/di/AppModule.kt | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/gravatar/app/GravatarApplication.kt b/app/src/main/java/com/gravatar/app/GravatarApplication.kt index f4f196fa..5b28f6df 100644 --- a/app/src/main/java/com/gravatar/app/GravatarApplication.kt +++ b/app/src/main/java/com/gravatar/app/GravatarApplication.kt @@ -1,7 +1,6 @@ package com.gravatar.app import android.app.Application -import com.gravatar.analytics.di.analyticsModule import com.gravatar.app.di.appModule import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -13,7 +12,7 @@ class GravatarApplication : Application() { startKoin { androidContext(this@GravatarApplication) - modules(appModule, analyticsModule) + modules(appModule) } } } diff --git a/app/src/main/java/com/gravatar/app/di/AppModule.kt b/app/src/main/java/com/gravatar/app/di/AppModule.kt index 5edfaa32..9c62a323 100644 --- a/app/src/main/java/com/gravatar/app/di/AppModule.kt +++ b/app/src/main/java/com/gravatar/app/di/AppModule.kt @@ -1,5 +1,6 @@ package com.gravatar.app.di +import com.gravatar.analytics.di.analyticsModule import com.gravatar.app.homeUi.di.homeUiModule import com.gravatar.app.loginUi.di.loginUiModule import org.koin.dsl.module @@ -8,5 +9,6 @@ val appModule = module { includes( homeUiModule, loginUiModule, + analyticsModule, ) } From 81cbb12ef5a5a8644d2878a1017f9c43bedab5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Thu, 19 Jun 2025 10:10:49 +0200 Subject: [PATCH 11/12] Using WPCOM userType --- .../gravatar/analytics/tracks/TracksTracker.kt | 6 ++++-- .../analytics/tracks/TrackTrackerTest.kt | 17 ++++++++++++++--- .../main/java/com/gravatar/app/MainActivity.kt | 10 ++++++++-- .../java/com/gravatar/app/analytics/AppEvent.kt | 2 +- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt index 1e2981e2..dfd86320 100644 --- a/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt +++ b/analytics/src/main/kotlin/com/gravatar/analytics/tracks/TracksTracker.kt @@ -10,8 +10,10 @@ internal class TracksTracker(private val tracksClient: TracksClient) : Tracker() private val anonId: String = generateNewAnonID() override fun trackEvent(event: Event) { - // We should add GRAVATAR userType when available. - tracksClient.track(event.name, userId ?: anonId, TracksClient.NosaraUserType.ANON) + val userType = userId?.let { + TracksClient.NosaraUserType.WPCOM + } ?: TracksClient.NosaraUserType.ANON + tracksClient.track(event.name, userId ?: anonId, userType) } override fun flush() { diff --git a/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt index 703719df..ac63d39c 100644 --- a/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt +++ b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt @@ -19,15 +19,26 @@ class TrackTrackerTest { } @Test - fun `when trackEvent is invoked then call track on client`() { - tracker.userId = "test_user_id" + fun `when trackEvent is invoked without userId then call track on client with ANON`() { val event = object : Event { override val name: String = "test_event" } tracker.trackEvent(event) - verify { mockClient.track(event.name, "test_user_id", TracksClient.NosaraUserType.ANON) } + verify { mockClient.track(event.name, any(), TracksClient.NosaraUserType.ANON) } + } + + @Test + fun `when trackEvent is invoked with userId then call track on client with GRAVATAR`() { + val event = object : Event { + override val name: String = "test_event_with_user" + } + tracker.userId = "someUserId" + + tracker.trackEvent(event) + + verify { mockClient.track(event.name, "someUserId", TracksClient.NosaraUserType.WPCOM) } } @Test diff --git a/app/src/main/java/com/gravatar/app/MainActivity.kt b/app/src/main/java/com/gravatar/app/MainActivity.kt index ef0fc3ca..3700f39e 100644 --- a/app/src/main/java/com/gravatar/app/MainActivity.kt +++ b/app/src/main/java/com/gravatar/app/MainActivity.kt @@ -18,12 +18,18 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() - tracker.trackEvent(AppEvent.Test) - setContent { GravatarTheme { RootNavigation() } } } + + override fun onResume() { + super.onResume() + // Remove this lines when we have the first real event to track. + tracker.userId = "hamorillo" + tracker.trackEvent(AppEvent.Test) + tracker.flush() + } } diff --git a/app/src/main/java/com/gravatar/app/analytics/AppEvent.kt b/app/src/main/java/com/gravatar/app/analytics/AppEvent.kt index e6ac8bf8..6de994f7 100644 --- a/app/src/main/java/com/gravatar/app/analytics/AppEvent.kt +++ b/app/src/main/java/com/gravatar/app/analytics/AppEvent.kt @@ -5,6 +5,6 @@ import com.gravatar.analytics.Event sealed class AppEvent : Event { data object Test : AppEvent() { - override val name: String = "gravatar_android_test" + override val name: String = "gravatarandroid_test_test" } } From 6a17b899096cf745beeb14564947eb8dd20b21ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=81ctor=20Abraham?= Date: Thu, 19 Jun 2025 10:19:01 +0200 Subject: [PATCH 12/12] Refactor TrackTrackerTest --- .../kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt index ac63d39c..82e4bdcf 100644 --- a/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt +++ b/analytics/src/test/kotlin/com/gravatar/analytics/tracks/TrackTrackerTest.kt @@ -4,6 +4,7 @@ import com.automattic.android.tracks.TracksClient import com.gravatar.analytics.Event import io.mockk.mockk import io.mockk.verify +import io.mockk.verifySequence import org.junit.Before import org.junit.Test @@ -45,6 +46,8 @@ class TrackTrackerTest { fun `when flush is invoked then call flush on client`() { tracker.flush() - verify(exactly = 1) { mockClient.flush() } + verifySequence { + mockClient.flush() + } } }