v1.2.14: Usage today (estimated) card + Persian guide localization

Adds a daily-budget visualization for users worried about hitting the
Apps Script free-tier quota (20,000 UrlFetchApp calls/day).

Usage today card (desktop + Android):
- today_calls / today_bytes / today_key / today_reset_secs atomics on
  DomainFronter, hooked into the bytes_relayed fetch_add path so we only
  count successful relays (matching what Google actually billed)
- Daily rollover at 00:00 UTC, std-only date math (Hinnant's
  civil_from_days) — no chrono/time dep pull
- StatsSnapshot extended with the four new fields + to_json() for the
  Android JNI bridge
- Desktop UI renders the card right under the existing Traffic stats
  with a hyperlink to https://script.google.com/home/usage for the
  authoritative Google-side number
- Android UI renders the same card via Compose, polling
  Native.statsJson(handle) once a second only while the proxy is up,
  with an Intent(ACTION_VIEW, …) opening the dashboard URL

JNI / state plumbing:
- New Java_…_statsJson reads the Arc<DomainFronter> kept in slot_map
- VpnState.proxyHandle StateFlow so HomeScreen knows which handle to
  poll without poking into the service's internal state
- MhrvVpnService publishes the handle on start, zeroes on teardown

Persian localization:
- HowToUseBody (5-step guide + Cloudflare Turnstile note) was
  hardcoded English even when locale=FA. Ported to a string resource
  with a full FA translation in values-fa/strings.xml. Persian users
  no longer drop to English at the bottom of the screen.

Also lands the deferred Android ConfigStore.kt wiring for
passthrough_hosts (commit fe9328e shipped the Rust + desktop side).

82 tests pass (added: unix_to_ymd_utc_handles_known_epochs,
seconds_until_utc_midnight_is_bounded). Built and visually verified on
both desktop and Android emulator (mhrv_test AVD).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
therealaleph
2026-04-24 20:15:52 +03:00
parent c416cd3639
commit d01e9f0f2f
14 changed files with 539 additions and 31 deletions
@@ -137,6 +137,7 @@ class MhrvVpnService : VpnService() {
// backgrounding. Issue #37.
if (cfg.connectionMode == ConnectionMode.PROXY_ONLY) {
Log.i(TAG, "PROXY_ONLY mode: listeners up, skipping VpnService/TUN")
VpnState.setProxyHandle(proxyHandle)
VpnState.setRunning(true)
return
}
@@ -258,6 +259,7 @@ class MhrvVpnService : VpnService() {
// to observe. Only flipped true once everything above succeeded —
// if we'd flipped it earlier the button would light up green for
// a failed-to-establish run.
VpnState.setProxyHandle(proxyHandle)
VpnState.setRunning(true)
}
@@ -339,6 +341,7 @@ class MhrvVpnService : VpnService() {
}
// Flip UI state last — the button reverts to Connect only after
// the native-side cleanup actually happened, not optimistically.
VpnState.setProxyHandle(0L)
VpnState.setRunning(false)
Log.i(TAG, "teardown: done")
}