From cabf9b7be36e2e12716e19a5262451a4186a8b27 Mon Sep 17 00:00:00 2001 From: Brayo Date: Sat, 19 Sep 2026 16:03:17 +0300 Subject: [PATCH 1/2] fix(android): open GitHub issue form from Report bugs --- .../net/activitywatch/android/MainActivity.kt | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/java/net/activitywatch/android/MainActivity.kt b/mobile/src/main/java/net/activitywatch/android/MainActivity.kt index 6a87ae90..3d40383c 100644 --- a/mobile/src/main/java/net/activitywatch/android/MainActivity.kt +++ b/mobile/src/main/java/net/activitywatch/android/MainActivity.kt @@ -104,6 +104,36 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } } + private fun openBugReport() { + val body = """ + ## Description + + Describe the problem here. + + ## Steps to reproduce + + 1. + + ## Expected behavior + + Describe what you expected to happen. + + ## Environment + + - App version: $version + - Android version: ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT}) + """.trimIndent() + val uri = Uri.parse("https://github.com/ActivityWatch/aw-android/issues/new") + .buildUpon() + .appendQueryParameter("body", body) + .build() + try { + startActivity(Intent(Intent.ACTION_VIEW, uri)) + } catch (e: ActivityNotFoundException) { + Snackbar.make(binding.root, R.string.no_browser_found, Snackbar.LENGTH_SHORT).show() + } + } + override fun onFragmentInteraction(item: Uri) { Log.w(TAG, "URI onInteraction listener not implemented") } @@ -336,8 +366,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte openDashboardInBrowser() } R.id.nav_send -> { - Snackbar.make(binding.coordinatorLayout, "The send button was clicked, but it's not yet implemented!", Snackbar.LENGTH_LONG) - .setAction("Action", null).show() + openBugReport() } } From 7d25951585e258c048fc10576346257aa8262033 Mon Sep 17 00:00:00 2001 From: Brayo Date: Sat, 19 Sep 2026 16:39:31 +0300 Subject: [PATCH 2/2] test(android): extract bugReportUrl helper and cover the prefilled issue URL Move the GitHub issue URL construction out of MainActivity.openBugReport into a pure bugReportUrl() function so the endpoint, report outline and environment details can be checked in plain JUnit tests without Android classes. Add tests for the decoded body, and for encoding of reserved characters in version strings. --- .../net/activitywatch/android/MainActivity.kt | 50 +++++++++++-------- .../android/MainActivityNavigationTest.kt | 33 ++++++++++++ 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/mobile/src/main/java/net/activitywatch/android/MainActivity.kt b/mobile/src/main/java/net/activitywatch/android/MainActivity.kt index 3d40383c..584756b3 100644 --- a/mobile/src/main/java/net/activitywatch/android/MainActivity.kt +++ b/mobile/src/main/java/net/activitywatch/android/MainActivity.kt @@ -30,6 +30,8 @@ import net.activitywatch.android.databinding.ActivityMainBinding import net.activitywatch.android.fragments.TestFragment import net.activitywatch.android.fragments.WebUIFragment import net.activitywatch.android.watcher.UsageStatsWatcher +import java.net.URLEncoder +import java.nio.charset.StandardCharsets private const val TAG = "MainActivity" @@ -53,6 +55,31 @@ internal fun initialWebUiUrl( internal fun shouldOpenActivityViewImmediately(openActivityView: Boolean, isResumed: Boolean): Boolean = openActivityView && isResumed +internal const val BUG_REPORT_ISSUE_URL = "https://github.com/ActivityWatch/aw-android/issues/new" + +/** Prefilled GitHub issue URL for the drawer "Report bugs" item. */ +internal fun bugReportUrl(appVersion: String, androidVersion: String, apiLevel: Int): String { + val body = """ + ## Description + + Describe the problem here. + + ## Steps to reproduce + + 1. + + ## Expected behavior + + Describe what you expected to happen. + + ## Environment + + - App version: $appVersion + - Android version: $androidVersion (API $apiLevel) + """.trimIndent() + return "$BUG_REPORT_ISSUE_URL?body=${URLEncoder.encode(body, StandardCharsets.UTF_8.name())}" +} + /** Native Home lives in MainActivity, so it inherits the last WebView chrome unless reset. */ internal fun shouldResetChromeForNativeDestination(isWebUiDestination: Boolean): Boolean = !isWebUiDestination @@ -105,28 +132,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } private fun openBugReport() { - val body = """ - ## Description - - Describe the problem here. - - ## Steps to reproduce - - 1. - - ## Expected behavior - - Describe what you expected to happen. - - ## Environment - - - App version: $version - - Android version: ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT}) - """.trimIndent() - val uri = Uri.parse("https://github.com/ActivityWatch/aw-android/issues/new") - .buildUpon() - .appendQueryParameter("body", body) - .build() + val uri = Uri.parse(bugReportUrl(version, Build.VERSION.RELEASE, Build.VERSION.SDK_INT)) try { startActivity(Intent(Intent.ACTION_VIEW, uri)) } catch (e: ActivityNotFoundException) { diff --git a/mobile/src/test/java/net/activitywatch/android/MainActivityNavigationTest.kt b/mobile/src/test/java/net/activitywatch/android/MainActivityNavigationTest.kt index 3fc25229..c4ea67ea 100644 --- a/mobile/src/test/java/net/activitywatch/android/MainActivityNavigationTest.kt +++ b/mobile/src/test/java/net/activitywatch/android/MainActivityNavigationTest.kt @@ -4,6 +4,8 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test +import java.net.URLDecoder +import java.nio.charset.StandardCharsets class MainActivityNavigationTest { @Test @@ -113,4 +115,35 @@ class MainActivityNavigationTest { ), ) } + + @Test + fun bugReportUrl_pointsAtNewIssueFormWithSingleBodyParam() { + val url = bugReportUrl(appVersion = "0.12.3", androidVersion = "14", apiLevel = 34) + + assertTrue(url.startsWith("$BUG_REPORT_ISSUE_URL?body=")) + assertFalse(url.substringAfter("?body=").contains("&")) + } + + @Test + fun bugReportUrl_bodyDecodesToOutlineWithEnvironmentDetails() { + val url = bugReportUrl(appVersion = "0.12.3", androidVersion = "14", apiLevel = 34) + val body = URLDecoder.decode(url.substringAfter("?body="), StandardCharsets.UTF_8.name()) + + assertTrue(body.startsWith("## Description\n")) + assertTrue(body.contains("## Steps to reproduce\n")) + assertTrue(body.contains("## Expected behavior\n")) + assertTrue(body.endsWith("## Environment\n\n- App version: 0.12.3\n- Android version: 14 (API 34)")) + } + + @Test + fun bugReportUrl_encodesReservedCharactersInVersions() { + val url = bugReportUrl(appVersion = "1.0-rc&1 #2", androidVersion = "?", apiLevel = 1) + val encoded = url.substringAfter("?body=") + val body = URLDecoder.decode(encoded, StandardCharsets.UTF_8.name()) + + assertFalse(encoded.contains("&")) + assertFalse(encoded.contains("#")) + assertFalse(encoded.contains("?")) + assertTrue(body.contains("- App version: 1.0-rc&1 #2\n- Android version: ? (API 1)")) + } }