Implement ACRA crash reports

This commit is contained in:
Torsten Grote
2025-10-10 10:43:34 -03:00
parent 3d135c7518
commit 98f0345268
8 changed files with 212 additions and 0 deletions

View File

@@ -152,6 +152,7 @@ jmdns = { module = "org.jmdns:jmdns", version.ref = "jmdns" }
zxing-core = { module = "com.google.zxing:core", version.ref = "zxingCore" }
acra-mail = { module = "ch.acra:acra-mail", version.ref = "acra" }
acra-dialog = { module = "ch.acra:acra-dialog", version.ref = "acra" }
acra-notification = { module = "ch.acra:acra-notification", version.ref = "acra" }
adapterdelegates4 = { module = "com.hannesdorfmann:adapterdelegates4", version.ref = "adapterdelegates4" }
bcprov-jdk15to18 = { module = "org.bouncycastle:bcprov-jdk15to18", version.ref = "bouncycastle" }
bcpkix-jdk15to18 = { module = "org.bouncycastle:bcpkix-jdk15to18", version.ref = "bouncycastle" }

View File

@@ -22,6 +22,9 @@ android {
}
buildTypes {
all {
buildConfigField("String", "ACRA_REPORT_EMAIL", "\"t+fdroidnext@grobox.de\"")
}
release {
isMinifyEnabled = false
proguardFiles(
@@ -83,6 +86,9 @@ dependencies {
implementation(libs.logback.android)
implementation(libs.microutils.kotlin.logging)
implementation(libs.acra.mail)
implementation(libs.acra.dialog)
implementation(libs.hilt.android)
implementation(libs.androidx.hilt.navigation.compose)
ksp(libs.hilt.android.compiler)

View File

@@ -67,6 +67,12 @@
<data android:pathPattern="/.*/packages/.*" />
</intent-filter>
</activity>
<activity
android:name="org.fdroid.ui.CrashActivity"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
android:process=":acra" />
<service
android:name="org.fdroid.install.AppInstallService"

View File

@@ -17,12 +17,22 @@ import coil3.request.Options
import coil3.request.crossfade
import coil3.util.DebugLogger
import dagger.hilt.android.HiltAndroidApp
import org.acra.ACRA
import org.acra.ReportField
import org.acra.config.dialog
import org.acra.config.mailSender
import org.acra.data.StringFormat.JSON
import org.acra.ktx.initAcra
import org.fdroid.download.DownloadRequest
import org.fdroid.download.LocalIconFetcher
import org.fdroid.download.PackageName
import org.fdroid.download.coil.DownloadRequestFetcher
import org.fdroid.next.BuildConfig
import org.fdroid.next.BuildConfig.APPLICATION_ID
import org.fdroid.next.BuildConfig.VERSION_NAME
import org.fdroid.next.R
import org.fdroid.repo.RepoUpdateWorker
import org.fdroid.ui.CrashActivity
import org.fdroid.updates.AppUpdateWorker
import javax.inject.Inject
@@ -40,12 +50,47 @@ class App : Application(), Configuration.Provider, SingletonImageLoader.Factory
.setWorkerFactory(workerFactory)
.build()
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
initAcra {
reportFormat = JSON
reportContent = listOf(
ReportField.USER_COMMENT,
ReportField.PACKAGE_NAME,
ReportField.APP_VERSION_NAME,
ReportField.ANDROID_VERSION,
ReportField.PRODUCT,
ReportField.BRAND,
ReportField.PHONE_MODEL,
ReportField.DISPLAY,
ReportField.TOTAL_MEM_SIZE,
ReportField.AVAILABLE_MEM_SIZE,
ReportField.CUSTOM_DATA,
ReportField.STACK_TRACE_HASH,
ReportField.STACK_TRACE,
)
reportSendFailureToast = getString(R.string.crash_report_error)
sendReportsInDevMode = true
dialog {
reportDialogClass = CrashActivity::class.java
}
mailSender {
mailTo = BuildConfig.ACRA_REPORT_EMAIL
subject = "$APPLICATION_ID $VERSION_NAME: Crash Report"
reportFileName = "ACRA-report.stacktrace.json"
}
}
}
@OptIn(ExperimentalComposeRuntimeApi::class)
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.SourceInformation)
}
// bail out here if we are the ACRA process to not initialize anything in crash process
if (ACRA.isACRASenderServiceProcess()) return
RepoUpdateWorker.scheduleOrCancel(applicationContext)
AppUpdateWorker.scheduleOrCancel(applicationContext)
}

View File

@@ -3,6 +3,7 @@ package org.fdroid.ui
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
@@ -114,6 +115,13 @@ fun AboutHeader(modifier: Modifier = Modifier) {
modifier = Modifier
.padding(top = 16.dp)
.alpha(0.75f)
.combinedClickable(
onClick = { },
onLongClick = {
// intentional crash for testing crash reporter, could be moved elsewhere
error("BOOOM!")
},
)
)
}
}

View File

@@ -0,0 +1,126 @@
package org.fdroid.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.acra.dialog.CrashReportDialogHelper
import org.fdroid.fdroid.ui.theme.FDroidContent
import org.fdroid.next.R
class CrashActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val helper = CrashReportDialogHelper(this, intent)
setContent {
FDroidContent {
CrashContent(
onCancel = {
helper.cancelReports()
finishAfterTransition()
},
onSend = { comment, userEmail ->
helper.sendCrash(comment, userEmail)
finishAfterTransition()
},
modifier = Modifier.safeContentPadding()
)
}
}
}
}
@Composable
private fun CrashContent(
onCancel: () -> Unit,
onSend: (String, String) -> Unit,
modifier: Modifier = Modifier
) {
val scrollState = rememberScrollState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween,
modifier = modifier
.fillMaxSize()
.padding(horizontal = 16.dp)
.verticalScroll(scrollState)
) {
Image(
painter = painterResource(id = R.drawable.ic_crash),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
contentDescription = null, // decorative element
modifier = Modifier
.fillMaxWidth(0.5f)
.aspectRatio(1f)
.padding(vertical = 16.dp)
)
val textFieldState = rememberTextFieldState()
Column(verticalArrangement = spacedBy(16.dp)) {
Text(
text = stringResource(R.string.crash_dialog_title),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Text(
text = stringResource(R.string.crash_report_text),
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier
)
TextField(
state = textFieldState,
placeholder = { Text(stringResource(R.string.crash_report_comment_hint)) },
modifier = Modifier.fillMaxWidth()
)
}
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
) {
OutlinedButton(onClick = onCancel) {
Text(stringResource(R.string.cancel))
}
Button(onClick = {
onSend(textFieldState.text.toString(), "")
}) {
Text(stringResource(R.string.crash_report_button_send))
}
}
}
}
@Preview
@Composable
private fun Preview() {
FDroidContent {
CrashContent({}, { _, _ -> })
}
}

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#ffffff"
android:pathData="M2.613,1.236A1.25,1.25 0,0 0,1.602 3.266L5.205,7.93C5.078,8.263 5.002,8.621 5.002,9L5.002,13L5.002,15.865L5.002,16C5.002,17.662 6.34,19 8.002,19L40.002,19C41.664,19 43.002,17.662 43.002,16L43.002,12L43.002,9.135L43.002,9C43.002,8.62 42.925,8.26 42.797,7.926L46.398,3.266A1.25,1.25 0,0 0,45.35 1.238A1.25,1.25 0,0 0,44.42 1.736L40.988,6.178C40.678,6.07 40.35,6 40.002,6L8.002,6C7.653,6 7.324,6.069 7.014,6.178L3.58,1.736A1.25,1.25 0,0 0,2.613 1.236zM14.375,9.75A3.375,3.375 0,0 1,17.75 13.125A3.375,3.375 0,0 1,14.375 16.5A3.375,3.375 0,0 1,11 13.125A3.375,3.375 0,0 1,14.375 9.75zM33.875,9.75A3.375,3.375 0,0 1,37.25 13.125A3.375,3.375 0,0 1,33.875 16.5A3.375,3.375 0,0 1,30.5 13.125A3.375,3.375 0,0 1,33.875 9.75z" />
<path
android:fillColor="#ffffff"
android:pathData="M8.002,20C7.794,20 7.592,20.021 7.396,20.061C6.027,20.337 5.002,21.53 5.002,22.971L5.002,23L5.002,42.779L5.002,42.805L5.002,42.996C5.002,44.658 6.34,45.996 8.002,45.996L40.002,45.996C41.664,45.996 43.002,44.658 43.002,42.996L43.002,22.996L43.002,22.971C43.002,21.53 41.977,20.337 40.607,20.061C40.412,20.021 40.21,20 40.002,20L8.002,20zM13.561,24L19.604,24L23.949,29.934L28.498,24L34.406,24L26.768,32.828L34.711,42.025L28.668,42.025L23.916,35.77L19.197,42.025L13.289,42.025L21.098,32.951L13.561,24z" />
</vector>

View File

@@ -88,4 +88,12 @@
<string name="onboarding_app_list_filter_message">Here you can apply filters to the list of apps, e.g. showing only apps within a certain category or repository. Changing the sort order is also possible.</string>
<string name="got_it">Got it</string>
<string name="crash_report_text">An unexpected error occurred.
This is not your fault.
Would you like to e-mail the details to help us fix the issue?
</string>
<string name="crash_report_comment_hint">Tell us what happened right before the crash</string>
<string name="crash_report_button_send">Send report</string>
<string name="crash_report_error">Could not send report. No email program found :(</string>
</resources>