From 37452be3bd6b4ee820b0ef78e2e12bcf049e540c Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 22 Jun 2026 10:45:54 +0200 Subject: [PATCH] feat(crash): file reports via the public Codeberg tracker instead of email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that a public, writable issue tracker exists (the Codeberg mirror at codeberg.org/jlmakiola/calendula, which the Gitea issue tab links to), revert the email hand-off (6150ce6) back to the in-app issue-creation flow: the crash path opens a prefilled issues/new page (with clipboard copy as the long-report fallback) and the manual "Report a problem" path opens the issue template chooser. The email pivot existed only because the personal Gitea has no public issue creation (reporters hit a login wall). Codeberg lets anyone register and file, so that reason is gone. Still no INTERNET permission — the user submits via the browser themselves. URLs point at Codeberg directly so the prefilled title/body survive (a Gitea external-tracker redirect wouldn't carry query params). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../calendula/ui/crash/CrashReportSubmit.kt | 67 ++++++++++--------- .../calendula/ui/settings/SettingsScreen.kt | 12 ++-- app/src/main/res/values-de/strings.xml | 9 ++- app/src/main/res/values/strings.xml | 14 ++-- 4 files changed, 53 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/crash/CrashReportSubmit.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/crash/CrashReportSubmit.kt index 80b7259..1477beb 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/crash/CrashReportSubmit.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/crash/CrashReportSubmit.kt @@ -9,44 +9,25 @@ import androidx.core.net.toUri import de.jeanlucmakiola.calendula.R /** - * Hand the captured crash report off to the user's email app: the report is - * copied to the clipboard (a safety net) and a pre-addressed email is composed - * with the report as the body. Nothing is sent automatically — the app has no - * network access; the user reviews and sends the mail themselves. - * - * Email is used rather than the web issue tracker because filing a Gitea issue - * requires an account on the instance, which anonymous reporters don't have — - * whereas anyone can send an email from the app they already use. + * Hand the captured crash report off to the user's chosen channel: the report + * is copied to the clipboard (the reliable path for a full stack trace) and the + * project's Gitea "new issue" page is opened with the body prefilled. Nothing is + * sent automatically — the app has no network access; the user reviews and + * submits the issue themselves. */ fun submitCrashReport(context: Context, report: String) { copyReportToClipboard(context, report) - val subject = context.getString(R.string.crash_report_issue_title) - val body = context.getString(R.string.crash_report_body_template, "```\n$report\n```") - val opened = sendReportEmail(context, subject, body) + val opened = runCatching { + context.startActivity(Intent(Intent.ACTION_VIEW, buildIssueUri(context, report))) + }.isSuccess val message = if (opened) R.string.crash_report_copied else R.string.crash_report_open_failed Toast.makeText(context, message, Toast.LENGTH_LONG).show() } -/** Compose a fresh problem-report email for a manual (non-crash) report. */ -fun reportIssueByEmail(context: Context) { - val subject = context.getString(R.string.report_issue_subject) - val body = context.getString(R.string.report_issue_body_template) - sendReportEmail(context, subject, body) -} - -/** - * Open the user's email app with a pre-addressed, prefilled message. The - * `mailto:` data URI guarantees only email apps resolve the intent (a plain - * ACTION_SEND would also offer messengers). The body rides in an extra rather - * than the URI, so there is no URL-length ceiling on the report. - */ -private fun sendReportEmail(context: Context, subject: String, body: String): Boolean { - val email = context.getString(R.string.report_issue_email) - val intent = Intent(Intent.ACTION_SENDTO, "mailto:$email".toUri()).apply { - putExtra(Intent.EXTRA_SUBJECT, subject) - putExtra(Intent.EXTRA_TEXT, body) - } - return runCatching { context.startActivity(intent) }.isSuccess +/** Open the issue tracker's template chooser for a manual (non-crash) report. */ +fun openIssueTracker(context: Context) { + val uri = context.getString(R.string.report_issue_choose_url).toUri() + runCatching { context.startActivity(Intent(Intent.ACTION_VIEW, uri)) } } private fun copyReportToClipboard(context: Context, report: String) { @@ -54,3 +35,27 @@ private fun copyReportToClipboard(context: Context, report: String) { val label = context.getString(R.string.crash_report_clip_label) clipboard.setPrimaryClip(ClipData.newPlainText(label, report)) } + +/** + * The Gitea `issues/new` URL with `title` and `body` prefilled. A full report + * can blow past URL-length limits, so an over-long one is left out of the link + * (with a "paste from clipboard" placeholder) — the clipboard copy is the + * source of truth in that case. + */ +private fun buildIssueUri(context: Context, report: String) = + context.getString(R.string.report_issue_url).toUri().buildUpon() + .appendQueryParameter("title", context.getString(R.string.crash_report_issue_title)) + .appendQueryParameter("body", buildIssueBody(context, report)) + .build() + +private fun buildIssueBody(context: Context, report: String): String { + val block = if (report.length > MAX_URL_REPORT_CHARS) { + context.getString(R.string.crash_report_body_paste) + } else { + "```\n$report\n```" + } + return context.getString(R.string.crash_report_body_template, block) +} + +/** Keep the prefilled body comfortably under common URL-length ceilings. */ +private const val MAX_URL_REPORT_CHARS = 6_000 diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt index f7c6cc6..4d1a06f 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt @@ -80,7 +80,7 @@ import de.jeanlucmakiola.calendula.data.prefs.ThemeMode import de.jeanlucmakiola.calendula.data.prefs.WeekStartPref import de.jeanlucmakiola.calendula.domain.EventFormField import de.jeanlucmakiola.calendula.ui.crash.CrashReportDialog -import de.jeanlucmakiola.calendula.ui.crash.reportIssueByEmail +import de.jeanlucmakiola.calendula.ui.crash.openIssueTracker import de.jeanlucmakiola.calendula.ui.crash.submitCrashReport import de.jeanlucmakiola.calendula.ui.common.CollapsingScaffold import de.jeanlucmakiola.calendula.ui.common.GroupedRow @@ -209,10 +209,10 @@ private fun SettingsHub( } /** - * Composes a problem-report email. If a crash report was captured (and not yet - * sent), it surfaces that report first via the same dialog the next-launch - * prompt uses; otherwise it opens a blank report email. No data leaves the - * device until the user sends the mail themselves. + * Opens the project's issue tracker to report a problem. If a crash report was + * captured (and not yet sent), it surfaces that report first via the same + * dialog the next-launch prompt uses; otherwise it opens the issue template + * chooser. No data leaves the device until the user submits the issue. */ @Composable private fun ReportProblemRow(position: Position) { @@ -226,7 +226,7 @@ private fun ReportProblemRow(position: Position) { leading = { CategoryIcon(Icons.Default.BugReport, ChipAccent.Neutral) }, onClick = { val pending = CrashReporter.pendingReport(context) - if (pending != null) report = pending else reportIssueByEmail(context) + if (pending != null) report = pending else openIssueTracker(context) }, ) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3fdeba9..4ec4c11 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -283,7 +283,7 @@ Version %1$s Calendula-App-Symbol Problem melden - Absturzbericht oder Feedback per E-Mail senden + Absturzbericht senden oder Issue-Tracker öffnen Kalender @@ -347,9 +347,8 @@ Nicht jetzt Absturzbericht Calendula-Absturzbericht - Bericht kopiert — E-Mail-App wird geöffnet - Keine E-Mail-App gefunden. Der Bericht ist in deiner Zwischenablage. + Bericht in die Zwischenablage kopiert + Der Issue-Tracker konnte nicht geöffnet werden. Der Bericht ist in deiner Zwischenablage. Danke, dass du einen Absturz in Calendula meldest. Bitte ergänze, was du gerade getan hast, und sende dann ab.\n\n### Was ist passiert\n\n\n### Absturzbericht\n%1$s\n - Calendula — Problemmeldung - Bitte beschreibe das aufgetretene Problem und was du gerade getan hast.\n\n + _(Der Bericht war zu lang für diesen Link — füge ihn aus deiner Zwischenablage hier ein.)_ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0be0675..b7c3e75 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -280,7 +280,7 @@ Version %1$s Calendula app icon Report a problem - Send a crash report or feedback by email + Send a crash report or open the issue tracker Calendars @@ -351,10 +351,10 @@ Not now Crash report Calendula crash report - Report copied — opening your email app - No email app found. The report is on your clipboard. - Thanks for reporting a crash in Calendula. Please add anything you remember about what you were doing, then send.\n\n### What happened\n\n\n### Crash report\n%1$s\n - mail@jeanlucmakiola.de - Calendula — problem report - Please describe the problem you ran into, and what you were doing when it happened.\n\n + Report copied to your clipboard + Couldn\'t open the issue tracker. The report is on your clipboard. + Thanks for reporting a crash in Calendula. Please add anything you remember about what you were doing, then submit.\n\n### What happened\n\n\n### Crash report\n%1$s\n + _(The report was too long for this link — paste it from your clipboard here.)_ + https://codeberg.org/jlmakiola/calendula/issues/new + https://codeberg.org/jlmakiola/calendula/issues/new/choose