- Routing: site and direct-address rules (sites resolved to addresses,
refreshed on connect and every 30 min), package patterns can be
switched off; unified Rules screen with add sheet and empty state;
"How traffic goes" for a site, IP or app with reasons, reachability and
one-tap rules; Recent apps (usage access) with failing apps; new-app
"go direct?" suggestions in Events.
- Diagnostics screen (all-in-one check), "Fastest" server, pause until
morning / until the network changes.
- Design system of the mockup: Manrope font, mockup palette for light and
dark, ShxKit components (cards, rows, switch, buttons, chips, tabs,
fields, sheets), mockup line icons; Home, nav bar, Routing, Rules,
Recent, Events, Diagnostics and route check rebuilt on it; shared
components restyled so the remaining screens pick up the look.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- EventLog keeps what the app noticed and did by itself for 30 days on the
device (events.json); repeats of the same event within 30 minutes are
merged (x3), problems that go away are marked resolved.
- Recorded: connection errors and recovery, carrier whitelists on/off,
health-check reconnects, auto-connect starting/parking the tunnel, the
end of a pause, available updates.
- Problems are pushed on a new "Connection problems" channel, but only
while the app is in the background (Home shows them already).
- «Events» screen (InboxActivity): filters (all, need attention,
connection, routes, app), grouped by day, one action per event; opened
from a bell with an unread badge in the Home header.
- Amber warning colours in the status palette, used for whitelists, no
network and an unresponsive server.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Connect tab gets the Shield-S header and, while connected, three quick
actions: pause, switch server, "site not loading?" (route check).
- Pause VPN for 5/15/30/60 min (PauseManager + PauseReceiver): disconnects
like a manual stop and reconnects on an alarm; auto-connect waits, any
connect request cancels the pause. Home shows a countdown, "turn on now"
and "+15 minutes".
- Connection errors become plain-language cards with one fixing action:
no internet (tracked via a non-VPN network callback), server not
responding (with the retry countdown and "other server"), server not
found, sign-in rejected, server not verified, generic failure.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- adaptive launcher icon as vectors: purple background, white shield with
the S cut out (even-odd), plus a monochrome layer for Android 13+ themed
icons; profile shortcuts use the same icon
- vector legacy icon for Android 7.x in mipmap-anydpi, old strongSwan PNGs
removed
- connected status bar icon is the Shield-S silhouette; connecting,
warning and disconnected states keep their outlined shields
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- HomeViewModel closes the profile database in finally blocks and relies
on the VPN_PROFILES_CHANGED broadcast instead of reloading twice
- READ_EXTERNAL_STORAGE only up to API 32; drop a dead settings flag
Co-Authored-By: Claude Opus 5.5 <[email protected]>
They can block for 10-30 s on a degraded network and delayed any profile
load (connect, retry, profile switch) queued behind them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- tile: query the configured profile once per listening session instead
of on every state notification
- widget: DISCONNECTING is shown as such, with a Disconnect button
- log viewer: bounded to 5000 lines, tails only while visible, picks up
a log created or restarted after opening, and no longer yanks the user
back to the bottom while they scroll up
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- computeExcludedPackages() (full PackageManager scan) runs on Default,
not Main.immediate, on config changes and package add/remove
- updateConfig() ignores no-op transforms and reconnects when routing is
switched off too (direct apps kept bypassing the tunnel until then)
- config is volatile and read after its version, so an old config can no
longer be cached under a new version
- Direct apps loads the app list once per entry, without GET_META_DATA
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- underlying-network bookkeeping is guarded by one lock and re-applied
after establish(), so a handover racing with it no longer leaves the
tunnel attributed to a lost network
- use the capabilities passed to onCapabilitiesChanged (no IPC per
signal-strength update); mCurrentProfile is volatile
- countdown-only notification re-posts are limited to one per 5 s
- the connected chronometer keeps its start time across re-posts
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- scheduled rechecks are forced: the 15/30 s steps were below FRESH_MS,
so the unforced recheck returned early and the backoff chain died
- signal-strength-only capability changes are no longer network events
(each one reset the backoff and launched a check)
- tunnel state transitions force a new round at most every 15 s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- traffic (1 s) and probe (10 s) loops run only while the Home screen is
shown and the VPN is connected; no 1 s idle tick otherwise
- the refresh button no longer runs a second exit-IP trace
- the glow pulse is not composed at all while disconnected or with
animations off (it redrew a transparent circle every frame)
- RTT sparkline appends each probe result once (it duplicated stale ones)
- Routing dead-host poll is lifecycle-aware
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- drop the in-process 12 h loop: the VPN keeps the process alive, so it
checked and pre-downloaded on battery, defeating the worker constraint
- the worker now waits for the check and a silent download it starts
- metered is judged by the physical network: the VPN reports itself
unmetered, so pre-downloads ran over cellular while connected
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- WITH_TUNNEL mode pings the server only once the external targets fail
(no ping fork + DNS lookup outside the tunnel on every healthy cycle;
an ICMP-blocking server no longer forces fast polls)
- initial battery-low state from the sticky ACTION_BATTERY_CHANGED
- non-state notifications no longer restart a pending cycle
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- same SHA-256 as the applied list: only bump the timestamp, no parse,
save or tunnel reconnect (it tore a healthy tunnel down daily)
- a failed download is not retried on every cold start (3 h backoff)
- the saved list is read and parsed off the main thread at startup
- switching the split off in system mode reconnects to drop the routes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Retrying, connecting or whitelist-blocked sessions kept the 60 s alarm and
the 10 min Doze wakeup for hours although a live process makes the alarm
useless. They now share the connected backoff (5 min / 60 min).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After a silent pre-download the state stayed Downloading, so tapping the
"update ready" notification did nothing. It now returns to Available;
an install request without a known update (e.g. after a process restart)
re-checks and installs when found.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- SimpleDateFormat is now used under the lock; rotation counts bytes
- AutoConnectManager.stop() clears pendingEvaluate, whose reset runnable
it removes, so later connects are not silently swallowed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- remove pending RETRY_MSG on reset/set: a Retry tap during a backoff
started a second countdown chain (double-speed ticks, two reconnects)
- notify listeners over a copy with per-listener error handling
- disconnect() tolerates the VPN service not running
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- DISCONNECT (plain startService) no longer promotes to the foreground,
and startForeground() refusals on Android 12+ are logged, not fatal
- drop the placeholder notification when nothing was running, so the
service does not stay in the foreground with a stale Disabled state
- onDestroy: shutdownNow/await the profile executor before closing the
database; profile loads catch errors instead of killing the process
- unbind by bind result, not by mService (binding leaked if destroyed
before onServiceConnected)
- the connection thread's crash guard can no longer throw itself
Co-Authored-By: Claude Opus 5.5 <[email protected]>
tcpPing never closed its socket: every health/whitelist/home probe leaked
an fd until GC and the peer's later FIN/RST woke the radio again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>