Skip to content
Open
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: 2 additions & 0 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ plugins {
alias(libs.plugins.com.diffplug.spotless) apply false
alias(libs.plugins.android.junit5) apply false
alias(libs.plugins.org.jetbrains.kotlin.plugin.compose) apply false
alias(libs.plugins.google.services) apply false
alias(libs.plugins.firebase.crashlytics) apply false
}

tasks.dokkaHtmlMultiModule {
Expand Down
21 changes: 0 additions & 21 deletions android/buildSrc/src/main/kotlin/project-properties.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ val requiredFhirProperties =
"OAUTH_CLIENT_ID",
"OAUTH_SCOPE",
"MAPBOX_SDK_TOKEN",
"SENTRY_DSN",
"OPENSRP_APP_ID",
"GEMINI_API_KEY",
"SPEECH_TO_TEXT_API_KEY",
Expand Down Expand Up @@ -58,23 +57,3 @@ requiredKeystoreProperties.forEach { property ->
project.extra.set(property, keystoreProperties.getProperty(property, "sample_$property"))
}

// Set Sentry properties
val requiredSentryProperties =
listOf(
"org",
"project",
"auth.token",
"url"
)

val sentryProperties = try {
readProperties((project.properties["sentryPropertiesFile"] ?: "${rootProject.projectDir}/sentry.properties").toString())
} catch (e: FileNotFoundException) {
if (project.properties["sentryPropertiesFile"] != null) {
throw e
} else Properties()
}

requiredSentryProperties.forEach { property ->
project.extra.set(property, sentryProperties.getProperty(property, "sentry_$property"))
}
8 changes: 8 additions & 0 deletions android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ uiautomator = "2.3.0"
work = "2.9.1"
xercesImpl = "2.12.2"
zip4j = "2.11.5"
firebase-bom = "34.2.0"
firebase-crashlytics-gradle = "3.0.6"
google-services = "4.4.3"

[libraries]
accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" }
Expand Down Expand Up @@ -226,6 +229,9 @@ workflow = { group = "org.smartregister", name = "workflow", version.ref = "fhir
xercesImpl = { group = "xerces", name = "xercesImpl", version.ref = "xercesImpl" }
androidx-fragment-compose = { group = "androidx.fragment", name = "fragment-compose", version.ref = "compose-fragment" }
zip4j = { module = "net.lingala.zip4j:zip4j", version.ref = "zip4j" }
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
firebase-crashlytics-ndk = { group = "com.google.firebase", name = "firebase-crashlytics-ndk" }
firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" }


[plugins]
Expand All @@ -239,6 +245,8 @@ org-jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "jetbrains" }
org-jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
org-owasp-dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "owasp" }
org-jetbrains-kotlin-plugin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
google-services = { id = "com.google.gms.google-services", version.ref = "google-services" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics-gradle" }

[bundles]
accompanist = ["accompanist-placeholder", "accompanist-flowlayout"]
Expand Down
157 changes: 5 additions & 152 deletions android/quest/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import com.android.build.api.variant.FilterConfiguration.FilterType
import io.sentry.android.gradle.extensions.InstrumentationFeature
import io.sentry.android.gradle.instrumentation.logcat.LogcatLevel
import java.io.FileReader
import java.text.SimpleDateFormat
import java.util.Date
Expand All @@ -22,10 +20,11 @@ plugins {
id("dagger.hilt.android.plugin")
id("androidx.navigation.safeargs")
id("org.sonarqube") version "3.5.0.2730"
id("io.sentry.android.gradle") version "3.11.1"
id("com.google.devtools.ksp")
id("com.google.dagger.hilt.android")
alias(libs.plugins.org.jetbrains.kotlin.plugin.compose)
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}

sonar {
Expand Down Expand Up @@ -77,7 +76,6 @@ android {
buildConfigField("String", "OAUTH_SCOPE", """"${project.extra["OAUTH_SCOPE"]}"""")
buildConfigField("String", "OPENSRP_APP_ID", """${project.extra["OPENSRP_APP_ID"]}""")
buildConfigField("String", "CONFIGURATION_SYNC_PAGE_SIZE", """"100"""")
buildConfigField("String", "SENTRY_DSN", """"${project.extra["SENTRY_DSN"]}"""")
buildConfigField("String", "BUILD_DATE", "\"$buildDate\"")
buildConfigField("String", "GEMINI_API_KEY", """"${project.extra["GEMINI_API_KEY"]}"""")
buildConfigField(
Expand Down Expand Up @@ -559,6 +557,9 @@ dependencies {
implementation(libs.androidx.fragment.compose)
implementation(libs.bundles.cameraX)
implementation(libs.log4j)
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics.ndk)
implementation(libs.firebase.analytics)

// AI dependencies
implementation(libs.google.cloud.speech) {
Expand Down Expand Up @@ -706,154 +707,6 @@ tasks.register("evaluatePerformanceBenchmarkResults") {
}
}

sentry {
// Disables or enables debug log output, e.g. for for sentry-cli.
// Default is disabled.
debug.set(true)

// The slug of the Sentry organization to use for uploading proguard mappings/source contexts.
org.set("""${project.extra["org"]}""")

// The slug of the Sentry project to use for uploading proguard mappings/source contexts.
projectName.set("""${project.extra["project"]}""")

// The authentication token to use for uploading proguard mappings/source contexts.
// WARNING: Do not expose this token in your build.gradle files, but rather set an environment
// variable and read it into this property.
authToken.set("""${project.extra["auth.token"]}""")

// The url of your Sentry instance. If you're using SAAS (not self hosting) you do not have to
// set this. If you are self hosting you can set your URL here
// url = null

// Disables or enables the handling of Proguard mapping for Sentry.
// If enabled the plugin will generate a UUID and will take care of
// uploading the mapping to Sentry. If disabled, all the logic
// related to proguard mapping will be excluded.
// Default is enabled.
includeProguardMapping.set(false)

// Whether the plugin should attempt to auto-upload the mapping file to Sentry or not.
// If disabled the plugin will run a dry-run and just generate a UUID.
// The mapping file has to be uploaded manually via sentry-cli in this case.
// Default is enabled.
autoUploadProguardMapping.set(false)

// Experimental flag to turn on support for GuardSquare's tools integration (Dexguard and
// External Proguard).
// If enabled, the plugin will try to consume and upload the mapping file produced by Dexguard
// and External Proguard.
// Default is disabled.
// dexguardEnabled.set(false)

// Disables or enables the automatic configuration of Native Symbols
// for Sentry. This executes sentry-cli automatically so
// you don't need to do it manually.
// Default is disabled.
uploadNativeSymbols.set(false)

// Whether the plugin should attempt to auto-upload the native debug symbols to Sentry or not.
// If disabled the plugin will run a dry-run.
// Default is enabled.
autoUploadNativeSymbols.set(true)

// Does or doesn't include the source code of native code for Sentry.
// This executes sentry-cli with the --include-sources param. automatically so
// you don't need to do it manually.
// Default is disabled.
includeNativeSources.set(false)

// Generates a JVM (Java, Kotlin, etc.) source bundle and uploads your source code to Sentry.
// This enables source context, allowing you to see your source
// code as part of your stack traces in Sentry.
includeSourceContext.set(false)

// Configure additional directories to be included in the source bundle which is used for
// source context. The directories should be specified relative to the Gradle module/project's
// root. For example, if you have a custom source set alongside 'main', the parameter would be
// 'src/custom/java'.
additionalSourceDirsForSourceContext.set(emptySet())

// Enable or disable the tracing instrumentation.
// Does auto instrumentation for specified features through bytecode manipulation.
// Default is enabled.
tracingInstrumentation {
enabled.set(true)

// Specifies a set of instrumentation features that are eligible for bytecode manipulation.
// Defaults to all available values of InstrumentationFeature enum class.
features.set(
setOf(
InstrumentationFeature.DATABASE,
InstrumentationFeature.FILE_IO,
InstrumentationFeature.OKHTTP,
InstrumentationFeature.COMPOSE,
),
)

// Enable or disable logcat instrumentation through bytecode manipulation.
// Default is enabled.
logcat {
enabled.set(true)

// Specifies a minimum log level for the logcat breadcrumb logging.
// Defaults to LogcatLevel.WARNING.
minLevel.set(LogcatLevel.WARNING)
}

// The set of glob patterns to exclude from instrumentation. Classes matching any of these
// patterns in the project's sources and dependencies JARs won't be instrumented by the
// Sentry Gradle plugin.
//
// Don't include the file extension. Filtering is done on compiled classes and
// the .class suffix isn't included in the pattern matching.
//
// Example usage:
// ```
// excludes.set(setOf("com/example/donotinstrument/**", "**/*Test"))
// ```
//
// Only supported when using Android Gradle plugin (AGP) version 7.4.0 and above.
// excludes.set(emptySet())
}

// Enable auto-installation of Sentry components (sentry-android SDK and okhttp, timber, fragment
// and compose integrations).
// Default is enabled.
// Only available v3.1.0 and above.
autoInstallation {
enabled.set(true)

// Specifies a version of the sentry-android SDK and fragment, timber and okhttp integrations.
//
// This is also useful, when you have the sentry-android SDK already included into a transitive
// dependency/module and want to
// align integration versions with it (if it's a direct dependency, the version will be
// inferred).
//
// NOTE: if you have a higher version of the sentry-android SDK or integrations on the
// classpath, this setting will have no effect
// as Gradle will resolve it to the latest version.
//
// Defaults to the latest published Sentry version.
sentryVersion.set("7.14.0")
}

// Disables or enables dependencies metadata reporting for Sentry.
// If enabled, the plugin will collect external dependencies and
// upload them to Sentry as part of events. If disabled, all the logic
// related to the dependencies metadata report will be excluded.
//
// Default is enabled.
includeDependenciesReport.set(true)

// Whether the plugin should send telemetry data to Sentry.
// If disabled the plugin won't send telemetry data.
// This is auto disabled if running against a self hosted instance of Sentry.
// Default is enabled.
// telemetry.set(true)
}

fun JSONObject.getTestName(): String {
val className = getString("className").substringAfterLast(".")
val methodName = getString("name").substringAfterLast("_")
Expand Down
3 changes: 0 additions & 3 deletions android/quest/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@
android:enabled="true"
tools:targetApi="29" />

<meta-data
android:name="io.sentry.auto-init"
android:value="false" />

<activity
android:name=".ui.appsetting.AppSettingActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,17 @@ package org.smartregister.fhircore.quest
import android.app.Application
import android.database.CursorWindow
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import com.google.android.fhir.datacapture.DataCaptureConfig
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.EntryPoint
import dagger.hilt.EntryPoints
import dagger.hilt.InstallIn
import dagger.hilt.android.HiltAndroidApp
import dagger.hilt.components.SingletonComponent
import io.sentry.android.core.SentryAndroid
import io.sentry.android.core.SentryAndroidOptions
import io.sentry.android.fragment.FragmentLifecycleIntegration
import java.net.URL
import javax.inject.Inject
import org.smartregister.fhircore.engine.data.remote.fhir.resource.ReferenceUrlResolver
import org.smartregister.fhircore.engine.util.extension.getSubDomain
import org.smartregister.fhircore.quest.data.QuestXFhirQueryResolver
import org.smartregister.fhircore.quest.ui.questionnaire.QuestionnaireItemViewHolderFactoryMatchersProviderFactoryImpl
import timber.log.Timber
Expand All @@ -61,9 +56,7 @@ class QuestApplication : Application(), DataCaptureConfig.Provider, Configuratio
Timber.plant(ReleaseTree())
}

if (BuildConfig.DEBUG.not()) {
initSentryMonitoring()
}
initFirebaseCrashlytics()

// TODO Fix this workaround for cursor size issue. Currently size set to 10 MB
try {
Expand All @@ -77,30 +70,12 @@ class QuestApplication : Application(), DataCaptureConfig.Provider, Configuratio
}
}

@VisibleForTesting
fun initSentryMonitoring(dsn: String = BuildConfig.SENTRY_DSN) {
if (dsn.isNotBlank()) {
val sentryConfiguration = { options: SentryAndroidOptions ->
options.dsn = dsn.trim { it <= ' ' }
// To set a uniform sample rate
options.tracesSampleRate = 1.0
options.isEnableUserInteractionTracing = true
options.isEnableUserInteractionBreadcrumbs = true
options.addIntegration(
FragmentLifecycleIntegration(
this,
enableFragmentLifecycleBreadcrumbs = true,
enableAutoFragmentLifecycleTracing = true,
),
)
try {
options.environment = URL(BuildConfig.FHIR_BASE_URL).getSubDomain().replace('-', '.')
} catch (e: Exception) {
Timber.e(e)
}
}

SentryAndroid.init(this, sentryConfiguration)
private fun initFirebaseCrashlytics() {
try {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
Timber.d("Firebase Crashlytics initialized successfully")
} catch (e: Exception) {
Timber.e(e, "Failed to initialize Firebase Crashlytics")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package org.smartregister.fhircore.quest

import android.util.Log
import android.util.Log.ERROR
import io.sentry.Sentry
import com.google.firebase.crashlytics.FirebaseCrashlytics
import timber.log.Timber

class ReleaseTree : Timber.Tree() {
Expand All @@ -33,7 +33,9 @@ class ReleaseTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, throwable: Throwable?) {
if (priority == ERROR || priority == Log.WARN || priority == Log.ASSERT) {
if (throwable != null) {
Sentry.captureException(throwable)
FirebaseCrashlytics.getInstance().recordException(throwable)
} else {
FirebaseCrashlytics.getInstance().log("$tag: $message")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import dagger.hilt.android.lifecycle.HiltViewModel
import io.sentry.Sentry
import io.sentry.protocol.User
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeUnit
Expand Down Expand Up @@ -68,7 +66,6 @@ import org.smartregister.fhircore.engine.util.extension.practitionerEndpointUrl
import org.smartregister.fhircore.engine.util.extension.removeHashPrefix
import org.smartregister.fhircore.engine.util.extension.showToast
import org.smartregister.fhircore.engine.util.extension.valueToString
import org.smartregister.fhircore.quest.BuildConfig
import org.smartregister.model.location.LocationHierarchy
import org.smartregister.model.practitioner.PractitionerDetails
import retrofit2.HttpException
Expand Down Expand Up @@ -171,16 +168,6 @@ constructor(
} else if (
accountAuthenticator.validateLoginCredentials(trimmedUsername, passwordAsCharArray)
) {
try {
// Configure Sentry scope
Sentry.configureScope { scope ->
scope.setTag("versionCode", BuildConfig.VERSION_CODE.toString())
scope.setTag("versionName", BuildConfig.VERSION_NAME)
scope.user = User().apply { username = trimmedUsername }
}
} catch (e: Exception) {
Timber.e(e)
}
_showProgressBar.postValue(false)
updateNavigateHome(true)
} else {
Expand Down
Loading
Loading