From fc2c7947870f2761652b10b3170fd3b0fc89c88a Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sun, 15 Mar 2026 08:36:44 +0000 Subject: feat: add file-based crash logger for offline diagnostics NavApplication installs an UncaughtExceptionHandler that writes crash stack traces to crash_latest.txt (and timestamped copies) in the app's external files dir. Readable without root or ADB. Co-Authored-By: Claude Sonnet 4.6 --- .../main/kotlin/org/terst/nav/NavApplication.kt | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 android-app/app/src/main/kotlin/org/terst/nav/NavApplication.kt (limited to 'android-app/app/src/main/kotlin/org/terst') diff --git a/android-app/app/src/main/kotlin/org/terst/nav/NavApplication.kt b/android-app/app/src/main/kotlin/org/terst/nav/NavApplication.kt new file mode 100644 index 0000000..1075930 --- /dev/null +++ b/android-app/app/src/main/kotlin/org/terst/nav/NavApplication.kt @@ -0,0 +1,52 @@ +package org.terst.nav + +import android.app.Application +import android.os.Environment +import java.io.File +import java.io.PrintWriter +import java.io.StringWriter +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class NavApplication : Application() { + + override fun onCreate() { + super.onCreate() + installCrashLogger() + } + + private fun installCrashLogger() { + val default = Thread.getDefaultUncaughtExceptionHandler() + Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> + try { + writeCrashLog(thread, throwable) + } catch (_: Exception) { + // never suppress the original crash + } + default?.uncaughtException(thread, throwable) + } + } + + private fun writeCrashLog(thread: Thread, throwable: Throwable) { + val sw = StringWriter() + throwable.printStackTrace(PrintWriter(sw)) + val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) + val body = buildString { + appendLine("=== Nav Crash Log ===") + appendLine("Time : $timestamp") + appendLine("Thread : ${thread.name}") + appendLine("Device : ${android.os.Build.MODEL} (API ${android.os.Build.VERSION.SDK_INT})") + appendLine() + append(sw) + } + + // Try external storage first (readable without root) + val dir = getExternalFilesDir(null) ?: filesDir + val file = File(dir, "crash_${timestamp}.txt") + file.writeText(body) + + // Also overwrite a fixed-name "latest" file for easy access + File(dir, "crash_latest.txt").writeText(body) + } +} -- cgit v1.2.3