diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4fe30b1..51d8dfb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "org.soulstone.vigil" minSdk = 26 targetSdk = 35 - versionCode = 4 - versionName = "0.1.3" + versionCode = 5 + versionName = "0.1.4" } // Fixed debug keystore committed to the repo (a debug key is non-secret — its diff --git a/app/src/main/kotlin/org/soulstone/vigil/MainActivity.kt b/app/src/main/kotlin/org/soulstone/vigil/MainActivity.kt index def4234..f2db6e0 100644 --- a/app/src/main/kotlin/org/soulstone/vigil/MainActivity.kt +++ b/app/src/main/kotlin/org/soulstone/vigil/MainActivity.kt @@ -95,7 +95,8 @@ class MainActivity : ComponentActivity() { onDistrust = { id -> lifecycleScope.launch { repo.clearBaseline(id) } }, - onRing = { tracker -> ringTracker(tracker) } + onRing = { tracker -> ringTracker(tracker) }, + onClearAll = { lifecycleScope.launch { repo.clearAll() } } ) } } diff --git a/app/src/main/kotlin/org/soulstone/vigil/data/TrackerRepository.kt b/app/src/main/kotlin/org/soulstone/vigil/data/TrackerRepository.kt index dde75cb..ddb0e2c 100644 --- a/app/src/main/kotlin/org/soulstone/vigil/data/TrackerRepository.kt +++ b/app/src/main/kotlin/org/soulstone/vigil/data/TrackerRepository.kt @@ -44,6 +44,13 @@ class TrackerRepository(private val db: VigilDatabase) { db.trackerDao().pruneStale(cutoff) } + /** Wipe the whole tracker list + sighting history (they re-populate as devices + * are re-detected). Keeps learned baseline places. */ + suspend fun clearAll() = mutex.withLock { + db.trackerDao().clearAll() + db.sightingDao().clearAll() + } + /** * Ingest one sighting. Persists it, updates the baseline, and re-evaluates the * tracker's risk. Returns the updated row and whether this crossed into ALERTING. diff --git a/app/src/main/kotlin/org/soulstone/vigil/data/db/VigilDatabase.kt b/app/src/main/kotlin/org/soulstone/vigil/data/db/VigilDatabase.kt index 56893e1..cbcb59e 100644 --- a/app/src/main/kotlin/org/soulstone/vigil/data/db/VigilDatabase.kt +++ b/app/src/main/kotlin/org/soulstone/vigil/data/db/VigilDatabase.kt @@ -91,6 +91,9 @@ interface TrackerDao { // stale, non-approved rows so the table and the UI list don't grow without bound. @Query("DELETE FROM trackers WHERE lastSeen < :cutoff AND approved = 0") suspend fun pruneStale(cutoff: Long) + + @Query("DELETE FROM trackers") + suspend fun clearAll() } @Dao @@ -103,6 +106,9 @@ interface SightingDao { @Query("DELETE FROM sightings WHERE timestamp < :cutoff") suspend fun prune(cutoff: Long) + + @Query("DELETE FROM sightings") + suspend fun clearAll() } @Dao diff --git a/app/src/main/kotlin/org/soulstone/vigil/ui/MainScreen.kt b/app/src/main/kotlin/org/soulstone/vigil/ui/MainScreen.kt index 333a077..958a6c9 100644 --- a/app/src/main/kotlin/org/soulstone/vigil/ui/MainScreen.kt +++ b/app/src/main/kotlin/org/soulstone/vigil/ui/MainScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Bluetooth import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.DeleteSweep import androidx.compose.material.icons.filled.GppMaybe import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Radar @@ -42,6 +43,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold @@ -84,7 +86,8 @@ fun MainScreen( onSetSensitivity: (Sensitivity) -> Unit, onApprove: (String, Boolean) -> Unit, onDistrust: (String) -> Unit, - onRing: (TrackerEntity) -> Unit + onRing: (TrackerEntity) -> Unit, + onClearAll: () -> Unit ) { var detail by remember { mutableStateOf(null) } var finding by remember { mutableStateOf(null) } @@ -109,6 +112,15 @@ fun MainScreen( } }, actions = { + if (trackers.isNotEmpty()) { + IconButton(onClick = onClearAll) { + Icon( + Icons.Filled.DeleteSweep, + contentDescription = "Clear all trackers", + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(end = 12.dp)) { Icon( Icons.Filled.Lock, null,