Skip to content
This repository was archived by the owner on Aug 21, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ local.properties
*.iml

# generated files
/build
build/
/captures
.kotlin/
1 change: 1 addition & 0 deletions analytics/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
18 changes: 18 additions & 0 deletions analytics/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
alias(libs.plugins.gravatar.android.library)
}

android {
namespace = "com.gravatar.analytics"
}

dependencies {

implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core)
implementation(libs.automattic.tracks)

testImplementation(libs.junit)
testImplementation(libs.koin.test.junit4)
testImplementation(libs.mockk.android)
}
Empty file added analytics/consumer-rules.pro
Empty file.
21 changes: 21 additions & 0 deletions analytics/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions analytics/src/main/kotlin/com/gravatar/analytics/Event.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.gravatar.analytics

interface Event {
val name: String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will likely need a way to pass some params as well.

}
7 changes: 7 additions & 0 deletions analytics/src/main/kotlin/com/gravatar/analytics/Tracker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.gravatar.analytics

abstract class Tracker {
abstract var userId: String?
abstract fun trackEvent(event: Event)
abstract fun flush()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module

val analyticsModule = module {
single { TracksClient.getClient(get()) }
singleOf(::TracksTracker) { bind<Tracker>() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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() {
override var userId: String? = null
private val anonId: String = generateNewAnonID()

override fun trackEvent(event: Event) {
val userType = userId?.let {
TracksClient.NosaraUserType.WPCOM
} ?: TracksClient.NosaraUserType.ANON
tracksClient.track(event.name, userId ?: anonId, userType)
}

override fun flush() {
tracksClient.flush()
}
}

private fun generateNewAnonID(): String {
// Generate a new UUID and return it as a string.
return UUID.randomUUID().toString()
}
Original file line number Diff line number Diff line change
@@ -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<TracksTracker>(Context::class)
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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 io.mockk.verifySequence
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<TracksClient>(relaxed = true)
tracker = TracksTracker(mockClient)
}

@Test
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, 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
fun `when flush is invoked then call flush on client`() {
tracker.flush()

verifySequence {
mockClient.flush()
}
}
}
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".GravatarApplication"
android:allowBackup="true"
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/com/gravatar/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import com.gravatar.analytics.Tracker
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

class MainActivity : ComponentActivity() {

private val tracker: Tracker by inject()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
Expand All @@ -18,4 +24,12 @@ class MainActivity : ComponentActivity() {
}
}
}

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()
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/gravatar/app/analytics/AppEvent.kt
Original file line number Diff line number Diff line change
@@ -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 = "gravatarandroid_test_test"
}
}
2 changes: 2 additions & 0 deletions app/src/main/java/com/gravatar/app/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -8,5 +9,6 @@ val appModule = module {
includes(
homeUiModule,
loginUiModule,
analyticsModule,
)
}
15 changes: 10 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
[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"
mockk = "1.14.2"
tracks = "6.0.3"

[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
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" }
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" }
Expand All @@ -37,11 +42,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" }
Expand Down
8 changes: 8 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
setUrl("https://a8c-libs.s3.amazonaws.com/android")
content {
includeGroup("com.automattic")
includeGroup("com.automattic.tracks")
}
}
}
}

rootProject.name = "Gravatar"
include(":app")
include(":homeUi")
include(":loginUi")
include(":analytics")