Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf15ead1ef | |||
| 6c460cbff7 |
@@ -13,8 +13,8 @@ android {
|
|||||||
applicationId = "org.soulstone.vigil"
|
applicationId = "org.soulstone.vigil"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 3
|
versionCode = 5
|
||||||
versionName = "0.1.2"
|
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
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.soulstone.vigil.ui
|
package org.soulstone.vigil.ui
|
||||||
|
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material.icons.filled.NotificationsActive
|
import androidx.compose.material.icons.filled.NotificationsActive
|
||||||
import androidx.compose.material.icons.filled.Sensors
|
import androidx.compose.material.icons.filled.Sensors
|
||||||
@@ -27,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
|
||||||
@@ -41,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
|
||||||
@@ -83,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) }
|
||||||
@@ -108,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,
|
||||||
@@ -432,6 +445,8 @@ private fun DetailRow(label: String, value: String) {
|
|||||||
* proximity meter. Works even on silent/modified tags that refuse to ring. */
|
* proximity meter. Works even on silent/modified tags that refuse to ring. */
|
||||||
@Composable
|
@Composable
|
||||||
private fun FinderScreen(t: TrackerEntity, onRing: (TrackerEntity) -> Unit, onClose: () -> Unit) {
|
private fun FinderScreen(t: TrackerEntity, onRing: (TrackerEntity) -> Unit, onClose: () -> Unit) {
|
||||||
|
// Intercept system back so it closes the finder instead of exiting the app.
|
||||||
|
BackHandler(onBack = onClose)
|
||||||
val rssi by ScanService.finderRssi.collectAsState()
|
val rssi by ScanService.finderRssi.collectAsState()
|
||||||
val smoothed = remember { mutableStateOf(-100f) }
|
val smoothed = remember { mutableStateOf(-100f) }
|
||||||
LaunchedEffect(rssi) { rssi?.let { smoothed.value = 0.35f * it + 0.65f * smoothed.value } }
|
LaunchedEffect(rssi) { rssi?.let { smoothed.value = 0.35f * it + 0.65f * smoothed.value } }
|
||||||
|
|||||||
Reference in New Issue
Block a user