d8670f4c32
Critical
--------
- DetectionService: subscribe to threatLevel + top event flows; rebuild the
foreground notification on every change so a locked-screen user sees
escalations. Vibrate on upward tier transitions (escalating waveforms for
YELLOW/ORANGE/RED), gated by Settings.vibrateOnAlert (default on).
- DetectionService: only mark _running=true if at least one scanner started;
stopSelf() if everything was disabled or denied. Switch START_STICKY →
START_NOT_STICKY so a system-killed service doesn't re-create into a
stuck "running but not scanning" state.
- DeflockClient: detect Overpass timeout-in-body (`{"remark": "...timed
out..."}`) and treat as failure — previously these 200-with-empty-elements
responses got cached for 24 h, hiding ALPRs in that 5×5 km cell for the
next day.
- DeflockScanner: record lastFetch coords + timestamp on BOTH success and
failure, with a 60 s backoff window after a failed attempt. Previously
`lastFetchLat` was only set on Success, so every subsequent location
update would re-trigger a 30 s POST that collectLatest then cancelled —
we'd never finish a fetch under sustained Overpass slowness.
- LocationProvider: stale-lastLocation race fix. The async `lastLocation`
callback now only seeds `_location` if it's still null and we're still
running — previously it could overwrite a fresher fix from
requestLocationUpdates, or fire after stop() and resurrect _location with
stale data.
Moderate
--------
- CitizenScanner: wait for the first non-null location with .first { } before
starting the poll/delay loop. First Citizen poll now fires within seconds
of the location fix, not up to 60 s after.
- MainScreen: when not running, show a muted gray circle with "IDLE" text
instead of the same solid green look as "scanning, all clear" — the
pulse animation was the only differentiator before.
- Compose state: rememberSaveable for the screen enum + bottom-sheet open
state, so SETTINGS survives rotation.
- MainActivity: detect permanently-denied permissions (the user picked
"don't ask again") via shouldShowRequestPermissionRationale. UI swaps the
call-to-action to "Open app settings" which fires
Settings.ACTION_APPLICATION_DETAILS_SETTINGS. onResume re-checks so a
user returning from app settings is reflected immediately.
Improvements
------------
- BLE/WiFi scanners record SourceHealth.OK on a successful start (and
FAILED with a specific reason on every short-circuit — disabled adapter,
missing permission, etc.) so the drill-down sheet is honest about radio
state, not just network state.
- DetectionEvent gains optional lat/lon (populated by DEFLOCK and CITIZEN);
SourceRow shows a tap-to-open-Maps icon next to events with coordinates,
firing a `geo:lat,lon?q=lat,lon(label)` Intent.
- SettingsScreen sliders use onValueChangeFinished — only commit to
SharedPreferences on drag-release, not on every pixel of movement.
- New Settings.vibrateOnAlert toggle (default on) wired to a SettingsScreen
row under a new "Alerts" section.
Minor
-----
- BleScanner iterates ALL manufacturer-data entries to find XUNTONG; only
falls back to the first entry if no XUNTONG match is present. Previously
we only inspected the first entry.
- Drop dead `?.` on JSONArray.optString in CitizenClient (returns String,
never null).
- Remove unused rememberCoroutineScope in MainScreen.
- Update stale Phase/Waze references in DetectionService comments.
- Add VIBRATE permission to manifest.
versionCode 6 → 7, versionName 0.1.5 → 0.1.6.
67 lines
1.6 KiB
Kotlin
67 lines
1.6 KiB
Kotlin
plugins {
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.kotlin.android)
|
|
alias(libs.plugins.kotlin.compose)
|
|
}
|
|
|
|
android {
|
|
namespace = "org.soulstone.overwatch"
|
|
compileSdk = 35
|
|
|
|
defaultConfig {
|
|
applicationId = "org.soulstone.overwatch"
|
|
minSdk = 26
|
|
targetSdk = 35
|
|
versionCode = 7
|
|
versionName = "0.1.6"
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
isMinifyEnabled = false
|
|
proguardFiles(
|
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
"proguard-rules.pro"
|
|
)
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
kotlin {
|
|
jvmToolchain(17)
|
|
}
|
|
|
|
buildFeatures {
|
|
compose = true
|
|
buildConfig = true
|
|
}
|
|
|
|
packaging {
|
|
resources {
|
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
|
}
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
implementation(libs.androidx.core.ktx)
|
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
implementation(libs.androidx.lifecycle.service)
|
|
implementation(libs.androidx.activity.compose)
|
|
|
|
implementation(platform(libs.androidx.compose.bom))
|
|
implementation(libs.androidx.compose.ui)
|
|
implementation(libs.androidx.compose.ui.graphics)
|
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
|
implementation(libs.androidx.compose.material3)
|
|
implementation(libs.androidx.compose.material.icons.extended)
|
|
|
|
implementation(libs.play.services.location)
|
|
|
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
|
}
|