mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-02-15 17:42:23 -05:00
Implement ACRA crash reports
This commit is contained in:
@@ -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" }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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!")
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
126
next/src/main/kotlin/org/fdroid/ui/CrashActivity.kt
Normal file
126
next/src/main/kotlin/org/fdroid/ui/CrashActivity.kt
Normal 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({}, { _, _ -> })
|
||||
}
|
||||
}
|
||||
12
next/src/main/res/drawable/ic_crash.xml
Normal file
12
next/src/main/res/drawable/ic_crash.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user