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 .configure
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"project_name": "Gravatar-Android",
"branch": "trunk",
"pinned_hash": "8045b892223ecaf5d62dd0cd2dc3165c6ffd44d7",
"pinned_hash": "3c8238c6aa7b0098bd4ccf24e0a7b6178e3ee2a7",
"files_to_copy": [
{
"file": "android/Gravatar-Android/secrets.properties",
Expand Down
Binary file modified .configure-files/secrets.properties.enc
Binary file not shown.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation(project(":clock"))
implementation(project(":design"))
implementation(project(":networkMonitor"))
implementation(project(":crashlogging"))

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/com/gravatar/app/GravatarApplication.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.gravatar.app

import android.app.Application
import com.automattic.android.tracks.crashlogging.CrashLogging
import com.gravatar.app.di.appModule
import org.koin.android.ext.android.get
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin

Expand All @@ -14,5 +16,8 @@ class GravatarApplication : Application() {
androidContext(this@GravatarApplication)
modules(appModule)
}

val crashLogging: CrashLogging = get()
crashLogging.initialize()
}
}
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
Expand Up @@ -5,6 +5,7 @@ import com.gravatar.app.clock.di.clockModule
import com.gravatar.app.homeUi.di.homeUiModule
import com.gravatar.app.loginUi.di.loginUiModule
import com.gravatar.app.networkmonitor.di.networkMonitorModule
import com.gravatar.crashlogging.di.crashLoggingModule
import org.koin.dsl.module

val appModule = module {
Expand All @@ -16,5 +17,6 @@ val appModule = module {
clockModule,
networkMonitorModule,
buildConfigModule,
crashLoggingModule,
)
}
1 change: 1 addition & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
compileOnly(libs.compose.compiler.gradlePlugin)
compileOnly(libs.detekt.gradlePlugin)
implementation(libs.roborazzi.gradlePlugin)
implementation(libs.sentry.gradlePlugin)
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import com.android.build.api.dsl.ApplicationExtension
import com.android.build.gradle.AppPlugin
import com.gravatar.app.configureDetekt
import com.gravatar.app.configureKotlinAndroid
import io.sentry.android.gradle.SentryPlugin
import io.sentry.android.gradle.extensions.SentryPluginExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
Expand All @@ -18,6 +20,7 @@ class GravatarAndroidApplicationConventionPlugin : Plugin<Project> {
with(target) {
apply<AppPlugin>()
apply<KotlinAndroidPluginWrapper>()
apply<SentryPlugin>()

extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this)
Expand All @@ -35,6 +38,8 @@ class GravatarAndroidApplicationConventionPlugin : Plugin<Project> {
checkDependencies = true
}
}

configureSentry()
configureDetekt()
}
}
Expand Down Expand Up @@ -76,4 +81,18 @@ class GravatarAndroidApplicationConventionPlugin : Plugin<Project> {
}
}
}

private fun Project.configureSentry() {
configure<SentryPluginExtension> {
org.set("a8c")
projectName.set("gravatar-android")
authToken.set(System.getenv("SENTRY_AUTH_TOKEN"))

includeProguardMapping.set(System.getenv("CI").toBoolean())
includeSourceContext.set(System.getenv("CI").toBoolean())

autoInstallation.enabled.set(false)
ignoredBuildTypes.set(listOf("debug"))
}
}
}
34 changes: 34 additions & 0 deletions crashlogging/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import java.util.Properties

plugins {
alias(libs.plugins.gravatar.android.library)
alias(libs.plugins.kotlin.android)
}

fun secretsProperties(): Properties {
return rootProject.extra["secretsProperties"] as Properties
}

android {
namespace = "com.gravatar.crashlogging"
buildFeatures.buildConfig = true

defaultConfig {
buildConfigField(
"String",
"SENTRY_DSN",
"\"${secretsProperties()["sentryDsn"]?.toString() ?: ""}\"",
)
}
}

dependencies {
implementation(project(":userComponent"))

implementation(libs.koin.core)
implementation(libs.koin.android)
implementation(libs.kotlinx.coroutines)
implementation(libs.androidx.core.ktx)

api(libs.automattic.crashlogging)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.gravatar.crashlogging

import android.app.Application
import androidx.core.os.ConfigurationCompat
import java.util.Locale

internal class ContextBasedLocaleProvider(
private val context: Application,
) : LocaleProvider {
override fun provideLocale(): Locale? {
return ConfigurationCompat.getLocales(context.resources.configuration)[0]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.gravatar.crashlogging

import com.automattic.android.tracks.crashlogging.CrashLoggingDataProvider
import com.automattic.android.tracks.crashlogging.CrashLoggingUser
import com.automattic.android.tracks.crashlogging.EventLevel
import com.automattic.android.tracks.crashlogging.ExtraKnownKey
import com.automattic.android.tracks.crashlogging.PerformanceMonitoringConfig
import com.automattic.android.tracks.crashlogging.ReleaseName
import com.gravatar.app.usercomponent.domain.repository.UserRepository
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map

internal class GravatarCrashLoggingDataProvider(
localeProvider: LocaleProvider,
userRepository: UserRepository
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm aware that this can circular DI dependency if crashlogging was about to be used in userComponent module. It's possible to address it one way or another, but until it won't be needed, I think there's no point to make this setup more complex now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Although not ideal and I would imagine pushing the new profile (or rather CrashLoggingUser) when updated outside of the module rather than depending on the :userComponent I think this is okay for now.

if crashlogging was about to be used in userComponent module.

I assume that fatal crashes are reported automatically. So that would only be needed if we wanted to report non-fatal ones, correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Although not ideal and I would imagine pushing the new profile

I get you, but I also like the reactiveness of this solution - it'd be great if code related to user wouldn't have to remember about pushing a new profile to things like "crash logging".

I assume that fatal crashes are reported automatically. So that would only be needed if we wanted to report non-fatal ones, correct?

True, you're correct! 👍

) : CrashLoggingDataProvider {

override val applicationContextProvider = emptyFlow<Map<String, String>>()

override val buildType = BuildConfig.BUILD_TYPE

override val enableCrashLoggingLogs = BuildConfig.DEBUG

override val locale = localeProvider.provideLocale()

override val performanceMonitoringConfig = PerformanceMonitoringConfig.Disabled

override val releaseName = if (BuildConfig.DEBUG) {
ReleaseName.SetByApplication(DEBUG_RELEASE_NAME)
} else {
ReleaseName.SetByTracksLibrary
}

override val sentryDSN = BuildConfig.SENTRY_DSN

override val user = userRepository.getProfile().map { profile ->
CrashLoggingUser(
userID = profile?.userId?.toString(),
email = profile?.hash,
username = profile?.displayName,
)
}

override fun crashLoggingEnabled() = false

override fun extraKnownKeys() = emptyList<ExtraKnownKey>()

override fun provideExtrasForEvent(
currentExtras: Map<ExtraKnownKey, String>,
eventLevel: EventLevel
) = emptyMap<ExtraKnownKey, String>()

override fun shouldDropWrappingException(
module: String,
type: String,
value: String
) = false

companion object {
const val DEBUG_RELEASE_NAME = "debug"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.gravatar.crashlogging

import java.util.Locale

internal fun interface LocaleProvider {
fun provideLocale(): Locale?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.gravatar.crashlogging.di

import com.automattic.android.tracks.crashlogging.CrashLoggingDataProvider
import com.automattic.android.tracks.crashlogging.CrashLoggingProvider
import com.gravatar.crashlogging.ContextBasedLocaleProvider
import com.gravatar.crashlogging.GravatarCrashLoggingDataProvider
import com.gravatar.crashlogging.LocaleProvider
import org.koin.android.ext.koin.androidApplication
import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.factoryOf
import org.koin.dsl.module

val crashLoggingModule = module {
factory<LocaleProvider> {
ContextBasedLocaleProvider(
context = androidApplication()
)
}
factoryOf(::GravatarCrashLoggingDataProvider) {
bind<CrashLoggingDataProvider>()
}
single {
CrashLoggingProvider.createInstance(
context = get(),
dataProvider = get(),
appScope = get()
)
}
}
5 changes: 4 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ retrofit = "3.0.0"
roborazzi = "1.45.1"
robolectric = "4.14.1"
room = "2.7.2"
tracks = "6.0.3"
tracks = "6.0.5"
turbine = "1.2.1"
browser = "1.8.0"
ktor = "3.1.3"
Expand All @@ -29,6 +29,7 @@ androidxTestCore = "1.6.1"
constraintLayout = "1.1.0"
qrose= "1.0.1"
moshi = "1.15.2"
sentry = "5.9.0"

[libraries]
kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinCoroutines" }
Expand All @@ -42,6 +43,7 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3"
androidx-constraintLayout-compose = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "constraintLayout" }
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" }
# Tracks - Analytics and Crash Reporting
automattic-crashlogging = { module = "com.automattic.tracks:crashlogging", version.ref = "tracks" }
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" }
Expand Down Expand Up @@ -90,6 +92,7 @@ kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-pl
compose-compiler-gradlePlugin = { group = "org.jetbrains.kotlin.plugin.compose", name = "org.jetbrains.kotlin.plugin.compose.gradle.plugin", version.ref = "kotlin" }
detekt-gradlePlugin = { group = "io.gitlab.arturbosch.detekt", name = "io.gitlab.arturbosch.detekt.gradle.plugin", version.ref = "detekt" }
roborazzi-gradlePlugin = { group = "io.github.takahirom.roborazzi", name = "io.github.takahirom.roborazzi.gradle.plugin", version.ref = "roborazzi" }
sentry-gradlePlugin = { group = "io.sentry", name = "sentry-android-gradle-plugin", version.ref = "sentry" }

[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
Expand Down
5 changes: 2 additions & 3 deletions loginUi/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import java.io.FileInputStream
import java.util.Properties

plugins {
Expand All @@ -7,7 +6,7 @@ plugins {
alias(libs.plugins.gravatar.android.compose)
}

fun localProperties(): Properties {
fun secretsProperties(): Properties {
return rootProject.extra["secretsProperties"] as Properties
}

Expand All @@ -16,7 +15,7 @@ android {

buildFeatures.buildConfig = true
defaultConfig {
localProperties().let { properties ->
secretsProperties().let { properties ->
buildConfigField(
"String",
"OAUTH_CLIENT_ID",
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ include(":clock")
include(":design")
include(":networkMonitor")
include(":api")
include(":crashlogging")
2 changes: 1 addition & 1 deletion userComponent/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dependencies {
implementation(libs.ktor.okhttp)
implementation(libs.ktor.content.negotiation)
implementation(libs.ktor.serialization.json)
implementation(libs.gravatar.core)
api(libs.gravatar.core)

// Room
implementation(libs.room.runtime)
Expand Down