From 60d051ee7b2540393031cdaddee197cccf603782 Mon Sep 17 00:00:00 2001 From: fiatcode Date: Mon, 4 May 2026 08:38:25 +0700 Subject: [PATCH] feat: add clear logs with confirmation dialog and fix DB dispatchers - Add clearLogs method to BridgeModule with proper coroutine scope - Fix fetchLogs and clearLogs using Thread+runBlocking which caused app close - Change serviceScope from Dispatchers.Main to Dispatchers.IO - Add error logging in fetchLogs for better diagnostics - Add clear logs button with confirmation dialog in StatusScreen --- .../dev/fiatcode/tracpulse/BridgeModule.kt | 49 +++++++++---------- lib/status_screen.dart | 39 +++++++++++++++ 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/android/app/src/main/kotlin/dev/fiatcode/tracpulse/BridgeModule.kt b/android/app/src/main/kotlin/dev/fiatcode/tracpulse/BridgeModule.kt index 7be79e5..28eefcb 100644 --- a/android/app/src/main/kotlin/dev/fiatcode/tracpulse/BridgeModule.kt +++ b/android/app/src/main/kotlin/dev/fiatcode/tracpulse/BridgeModule.kt @@ -11,8 +11,9 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler { @@ -174,30 +175,28 @@ class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler { } private fun fetchLogs(limit: Int, result: MethodChannel.Result) { - Thread { + CoroutineScope(Dispatchers.IO).launch { try { - val logs = runBlocking(Dispatchers.IO) { - val db = context?.let { AppDatabase.getInstance(it) } - if (db != null) { - val events = db.eventLogDao().getRecentEventsList(limit) - events.map { event -> - mapOf( - "id" to event.id, - "timestamp" to event.timestamp, - "eventType" to event.eventType, - "message" to event.message - ) - } - } else { - emptyList() + val db = context?.let { AppDatabase.getInstance(it) } + val logs = if (db != null) { + val events = db.eventLogDao().getRecentEventsList(limit) + events.map { event -> + mapOf( + "id" to event.id, + "timestamp" to event.timestamp, + "eventType" to event.eventType, + "message" to event.message + ) } + } else { + emptyList() } mainHandler.post { result.success(logs) } } catch (e: Exception) { Log.e(TAG, "fetchLogs failed", e) mainHandler.post { result.success(emptyList>()) } } - }.start() + } } private fun openBatteryOptimizationSettings(result: MethodChannel.Result) { @@ -234,21 +233,17 @@ class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler { } private fun clearLogs(result: MethodChannel.Result) { - Thread { + CoroutineScope(Dispatchers.IO).launch { try { - val deleted = runBlocking(Dispatchers.IO) { - val db = context?.let { AppDatabase.getInstance(it) } - if (db != null) { - db.eventLogDao().deleteAll() - } else { - 0 - } + val db = context?.let { AppDatabase.getInstance(it) } + if (db != null) { + db.eventLogDao().deleteAll() } - mainHandler.post { result.success(deleted) } + mainHandler.post { result.success(0) } } catch (e: Exception) { Log.e(TAG, "clearLogs failed", e) mainHandler.post { result.success(-1) } } - }.start() + } } } \ No newline at end of file diff --git a/lib/status_screen.dart b/lib/status_screen.dart index 3e70759..8793116 100644 --- a/lib/status_screen.dart +++ b/lib/status_screen.dart @@ -48,6 +48,45 @@ class _StatusScreenState extends State { } Future _clearLogs() async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + backgroundColor: const Color(0xFF1e1e1e), + title: const Text( + 'Clear Event Logs', + style: TextStyle(fontFamily: 'monospace', color: Color(0xFFe0e0e0)), + ), + content: const Text( + 'This will permanently delete all logged events. Continue?', + style: TextStyle(fontFamily: 'monospace', color: Color(0xFF9e9e9e)), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: const Text( + 'CANCEL', + style: TextStyle( + fontFamily: 'monospace', + color: Color(0xFF616161), + ), + ), + ), + TextButton( + onPressed: () => Navigator.pop(ctx, true), + child: const Text( + 'CLEAR', + style: TextStyle( + fontFamily: 'monospace', + color: Color(0xFFff5252), + ), + ), + ), + ], + ), + ); + + if (confirmed != true) return; + final result = await LocationBridge.clearLogs(); if (mounted) { if (result >= 0) {