Add 'Clear all' button to wipe the tracker list
Build APK / build (push) Waiting to run

Trash action in the top bar (shown when the list is non-empty) clears all tracker rows + sighting history via repo.clearAll(). Devices re-populate as they're re-detected; learned baseline places are kept. Release 0.1.4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187UiEtasyowhEBYs6s9iF5
This commit is contained in:
Kara Zajac
2026-07-15 12:42:06 -04:00
parent 6c460cbff7
commit bf15ead1ef
5 changed files with 30 additions and 4 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ android {
applicationId = "org.soulstone.vigil" applicationId = "org.soulstone.vigil"
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 4 versionCode = 5
versionName = "0.1.3" versionName = "0.1.4"
} }
// Fixed debug keystore committed to the repo (a debug key is non-secret — its // Fixed debug keystore committed to the repo (a debug key is non-secret — its
@@ -95,7 +95,8 @@ class MainActivity : ComponentActivity() {
onDistrust = { id -> onDistrust = { id ->
lifecycleScope.launch { repo.clearBaseline(id) } lifecycleScope.launch { repo.clearBaseline(id) }
}, },
onRing = { tracker -> ringTracker(tracker) } onRing = { tracker -> ringTracker(tracker) },
onClearAll = { lifecycleScope.launch { repo.clearAll() } }
) )
} }
} }
@@ -44,6 +44,13 @@ class TrackerRepository(private val db: VigilDatabase) {
db.trackerDao().pruneStale(cutoff) 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 * 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. * tracker's risk. Returns the updated row and whether this crossed into ALERTING.
@@ -91,6 +91,9 @@ interface TrackerDao {
// stale, non-approved rows so the table and the UI list don't grow without bound. // 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") @Query("DELETE FROM trackers WHERE lastSeen < :cutoff AND approved = 0")
suspend fun pruneStale(cutoff: Long) suspend fun pruneStale(cutoff: Long)
@Query("DELETE FROM trackers")
suspend fun clearAll()
} }
@Dao @Dao
@@ -103,6 +106,9 @@ interface SightingDao {
@Query("DELETE FROM sightings WHERE timestamp < :cutoff") @Query("DELETE FROM sightings WHERE timestamp < :cutoff")
suspend fun prune(cutoff: Long) suspend fun prune(cutoff: Long)
@Query("DELETE FROM sightings")
suspend fun clearAll()
} }
@Dao @Dao
@@ -28,6 +28,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bluetooth import androidx.compose.material.icons.filled.Bluetooth
import androidx.compose.material.icons.filled.CheckCircle 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.GppMaybe
import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Radar 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.FilterChip
import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@@ -84,7 +86,8 @@ fun MainScreen(
onSetSensitivity: (Sensitivity) -> Unit, onSetSensitivity: (Sensitivity) -> Unit,
onApprove: (String, Boolean) -> Unit, onApprove: (String, Boolean) -> Unit,
onDistrust: (String) -> Unit, onDistrust: (String) -> Unit,
onRing: (TrackerEntity) -> Unit onRing: (TrackerEntity) -> Unit,
onClearAll: () -> Unit
) { ) {
var detail by remember { mutableStateOf<TrackerEntity?>(null) } var detail by remember { mutableStateOf<TrackerEntity?>(null) }
var finding by remember { mutableStateOf<TrackerEntity?>(null) } var finding by remember { mutableStateOf<TrackerEntity?>(null) }
@@ -109,6 +112,15 @@ fun MainScreen(
} }
}, },
actions = { 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)) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(end = 12.dp)) {
Icon( Icon(
Icons.Filled.Lock, null, Icons.Filled.Lock, null,