Skip to content

2.7.1: audit fixes, accurate daily-recorded weeks, and a smaller APK - #52

Merged
1window2 merged 41 commits into
mainfrom
v2.7.1
Oct 25, 2026
Merged

1window2 merged 41 commits into
mainfrom
v2.7.1

Conversation

@1window2

Copy link
Copy Markdown
Owner

Summary

Fixes from a full-code audit of 2.7.0, a two-thirds smaller APK, and the follow-up fixes found on the device. See CHANGELOG.md for the complete list.

Fixed

  • Standby / scheduled recording: the scheduled screen no longer locks the phone about ten minutes after a finished run; every run starts the game fresh (a client resumed after hours in the background stopped at "reconnection failed"), waiting for the dismissed task's process to die; leaving the game only counts once it has been in front; Recents closing retries while Recents animates in; no crash without the battery dialog; servers reload after VPN consent.
  • Weekly tables (each with a failing regression test first): Gunsmoke weeks recorded once a day at 04:56 keep every day (search exhaustion now continues with safe interval bounds and exact weekly totals); the Sunday 05:00 capture closes the ending week; Standard days never exceed 90; mid-week joiners are credited from the day after their last absence; an open final Saturday keeps patrol unknown; comparisons across a reset are lower bounds.
  • Concurrency: capture publishes STOPPED only after teardown (new STOPPING phase; "Stop capture and continue" waits for it); CSV undo checks its digest where the checkpoint is installed; interrupted imports recover at startup and before capture; restore rollback runs under the same lock; startup recovery can no longer crash every launch.
  • Data: future-dated CSV rosters rejected; the database domain is excluded from backup and device transfer; inferred join/withdraw times use only activity between the surrounding rosters; weekly history ordered by id; a crash-left WAL is kept with the restore rollback copy; a re-registered Platoon is never deleted by a queued deletion; pending deletions finish off the main thread.
  • Capture / admission: unmetered VPN; the new-Platoon prompt stays open while confirming, shows failures, and follows the newest screen; replay binds every flow first so retries never duplicate history; stale .partial files cleaned; diagnostics bounded; TLS detected only by a full record header; native start fails cleanly when the thread cannot attach.
  • UI: notifications and background toasts follow the app language; Platoon/Weekly screens refresh on new rosters and keep scroll; weekly edit discard during save; profile-less screens survive a rename; cutline save off the main thread; packet copy/display limits; webhook removal confirmation and Keystore resilience; bar heights under system insets.

Changed

  • Release APK 3,186,124 → about 1.05 MB: emblem and launcher art as lossless WebP at drawn size, legacy launcher PNGs and unused metadata removed, v1 JAR signing off.
  • protocol is a plain Kotlin JVM module; CI skips the duplicate release lint pass; Pages deploys only on README changes.
  • Dead weekly render/note bookkeeping removed; member edits scan the Platoon's weeks once.

Testing

  • :protocol:test, :app:testDebugUnitTest, :app:lintDebug (68 warnings, down from 75), :app:compileDebugAndroidTestKotlin, signed :app:assembleRelease. New instrumented tests compile but were not run (they would wipe the debug app on the user's phone).
  • Emulator (Android 14, stand-in game packages): "Stop capture and continue" shows "Stopping capture" then continues; two-server standby with capture restart between servers; Recents close on the Pixel launcher; weekly table renders.
  • Galaxy Note10+ (Android 12), real HaoPlay Korea and Darkwinter Global: VPN reported NOT_METERED; both Platoons admitted; "Stop capture and continue" on CSV import; two-server standby rehearsal recorded both servers with fresh launches and closed each game; a HaoPlay card left in Recents was closed, its process killed, and the fresh launch recorded in 22 s; weekly table with real data.

… imports

- Remove the scheduled standby screen's delayed guards with it, and never lock from a closed screen
- Resolve capture notifications and app-context toasts in the app's language
- Reject roster CSV files dated in the future
- Exclude the databases domain from cloud backup and device transfer
- Ignore discard while weekly edits are saving and save the edits as tapped
- Guard a missing battery dialog and reload servers after VPN consent in standby
- Keep profile-less screens open when only the Platoon name or banner changes
- Resize the Platoon emblem sprites to their largest drawn size and store them, and the launcher art, as lossless WebP (2.77 MB to 0.30 MB)
- Drop legacy launcher PNGs that minSdk 26 never selects
- Stop v1 JAR signing and packaging unused Kotlin and androidx metadata
- Build the protocol module as a plain Kotlin JVM library
- Skip the duplicate release lint pass in CI and rebuild Pages only when the READMEs change

Release APK: 3,186,124 to 1,054,123 bytes.
- Keep only navigation in WeeklyReportStateHolder; background work already supersedes stale renders
- Export a single week read-only instead of recording a history revision
- Check for exportable weeks without building every report
- Require an explicit storage scope in per-Platoon preferences instead of a default that throws without a profile
A capture exactly at a reset records the closed previous game day. Add
PlatoonPeriods.checkpointDay for that rule and use it for the Gunsmoke
checkpoints and the weekly period snapshots, so the closing Sunday 05:00
capture finalizes the event score instead of being dropped.
The Standard solver dropped a capture exactly at the week's closing reset
and then overwrote the exact closing-boundary Saturday with an unknown
value. A reset capture now closes its previous day and pins that day's
final stage.
The active span began on the first observed day, which credited merit
earned after joining but before that capture to the wrong day. A member
missing from a roster can join right after it, so the span now starts on
the game day of the latest roster that omits the member.
Stopping used to mark the capture STOPPED as soon as it was requested, while
the native join, the queued-chunk drain and the session reset still ran on the
worker. Backup, CSV import/undo and profile edits gated on that phase could
overlap the live loop, for example a drained roster recreating a deleted
profile's database.

Add a STOPPING phase that lasts the whole teardown and publish STOPPED only
when it completes, including when the service is destroyed meanwhile.
CaptureGuard now requests the stop and continues once a listener observes
STOPPED, with a 20 second timeout that explains nothing was changed. Home shows
the stopping state, and standby restarts a capture that is still stopping and
only opens the game on a newer run.
A capture thread that failed AttachCurrentThread jumped to cleanup without
calling onCaptureStopped, because an unattached thread cannot call into Java.
The service stayed RUNNING over a dead VPN loop.

nativeStart now waits for the thread's attach outcome. On failure it joins the
thread, releases the context and returns false, which the service already
reports as a failed native start.
replaceRestoredState ran its rollback in a catch outside withExclusiveDatabase,
so another writer could take the lock between the failed install and the
rollback, and the rollback would then discard that write. Run the rollback
inside the same exclusive section.
restore() compared the post-import digest under one exclusive acquisition and
installed the checkpoint under another, so an ingest in between was silently
overwritten. Pass the expected digest down to restoreCheckpoint, which now
compares it inside the exclusive section that replaces the database and
replaces nothing on a mismatch. The earlier check stays as a fast failure
before any evidence is quarantined.
Interrupted-import recovery only ran when a CsvImportCheckpointManager was
constructed, which happens lazily from the Home undo row or the import flow.
Automatic recording could ingest into the scope first, and the later recovery
then restored the checkpoint over that evidence.

Recover every registered scope at process start on a background thread, and
again on the capture worker before any ingestion; the per-scope claim
serializes the two. Startup recovery, including the existing full-restore
recovery, now logs a failure and leaves its durable state for the next attempt
instead of crashing every launch.
A baseline accepted up to 15 minutes before the reset can leave the
previous day's login or patrol in the next day's delta, and a Daily Patrol
fact kept such a delta as an exact value above the cap. A patrol fact now
fixes the day at 90, and a delta above the cap keeps only its single valid
split or stays unknown.
The final-event aggregate copied a Saturday login or patrol miss from the
captured counters although both stay open until the 05:00 reset. Like the
day projection, it now applies only a positive consensus to the open day.
…bounds

A Merit This Week or Gunsmoke score counter that came back higher after
its Monday or event-start reset was reported as the small difference. The
comparison now knows the Platoon's reset zone and reports the newer counter
as a lower bound, shown with the usual ≥ and exported as a certainty column.
A day captured before its three attempts made the next transition
enumerate every tail score paired with every state, exhausting the run's
search budget and dropping every later day. Recording once a day at 04:56
hit this for most members.

The detailed search now skips tail score merits the interval's shared
rounding remainder rules out, checks its budget before a transition it
cannot finish, and caps partial states at 256. When it would stop, the run
continues on attempt and Login / Daily Patrol classes with closed-form merit
and score ranges, which admit every point split and stay conservative. Later
days keep their patrol and lower bounds and the final-event totals can stay
exact; a 50-member daily 04:56 week solves in well under 100 ms.
- Reload the Platoon and Weekly screens when a roster is saved while they are visible
- Clear the Platoon list after a failed load and rebind rows when the time zone or latest roster changes
- Restore list scroll only once rows exist
- Save weekly cutlines off the main thread and report a failed write instead of crashing
- Report a failed packet deletion, refuse clipboard copies over the binder limit, and show only the start of huge raw packets
- Ask before removing the Discord webhook and keep it through transient Keystore errors
- Add system-bar insets on top of the bars' minimum heights
- Stop Settings from rescheduling on an instance it is recreating, and ellipsize long capture notices
@1window2
1window2 merged commit c6858c1 into main Oct 25, 2026
5 checks passed
@1window2
1window2 deleted the v2.7.1 branch October 25, 2026 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant