Skip to content

2.5.0: refactor, bug fixes and UI/UX overhaul - #47

Merged
1window2 merged 130 commits into
mainfrom
v2.5.0
Oct 15, 2026
Merged

1window2 merged 130 commits into
mainfrom
v2.5.0

Conversation

@1window2

Copy link
Copy Markdown
Owner

Summary

Refactor, bug-fix and UI/UX overhaul for 2.5.0. Intended to be squash-merged; see CHANGELOG.md for the full list.

Capture

  • Native: fix a double free on repeated close callbacks, keep flow ids unique across captures, clear pending JNI exceptions, use the monotonic clock for timers, keep no state for untracked flows.
  • Parser: a malformed or cut-short continued roster is discarded instead of published as complete; frames are parsed in place without per-message copies.
  • Admission: quarantine a bound flow that reports a different unregistered Platoon, release claims on unexpected failures, keep the per-capture admission limit across service restarts.
  • Capture service split into a serial worker, flow engine, admission coordinator and notification helper; stopping never blocks the main thread.

Management data

  • Out-of-order captured rosters are replayed into history instead of withdrawing current members.
  • Game days follow local 05:00 across DST; rejoined members export their current period; unknown joins of withdrawal-only periods are editable.
  • CSV checkpoints retire atomically; weekly notes/overrides must stay inside their week; the live weekly table renders even if history recording fails; nested read-only exits no longer roll back outer transactions.
  • Database split into focused SQL objects behind one transactional owner.

UI/UX

  • Edge-to-edge screens with a shared ui toolkit (screen shell, top bar, controls, dialogs, background work).
  • Weekly grid: frozen member column, synced day columns, sticky header, opens at today, Apply, next day editing, cutline fills, top-three rank highlights, font-scaled rows.
  • Home: capture panel with client install state and live checklist, latest roster change summary, undo only when available, Recent/Saved packet history with in-place selection.
  • Higher contrast palette, single accent for tappable/selected items, neutral section headers, consistent type scale, Korean/English wording pass.
  • Guards against repeated send/share/backup/restore taps; unsaved-edit prompts; dialogs dismissed with their screen.

Dependencies

  • Added androidx.activity:activity:1.12.4 (only new dependency).
  • Kotlin 2.4.20-Beta1 -> stable 2.4.21. The CodeQL compatibility pin is unchanged.

Testing

  • :protocol:test, :app:testDebugUnitTest, :app:assembleDebug, :app:compileDebugAndroidTestKotlin, :app:lintDebug (0 errors) pass locally.
  • Instrumentation suite passed on an emulator for the management changes.
  • Debug build installed on a physical device (Android 12): capture start, client install state and the HaoPlay client through the VPN verified; in-game Platoon capture verification is still in progress.

Delete global references while the capture thread is still attached and
reap every capture thread exactly once, so a loop that exits on its own
no longer blocks the next start. Classify TLS/HTTP only on a flow's first
payload, notify Java only about TCP flows, and drop the always-false
sent flag from the payload callback.
A same-type continuation of a single-object payload such as 21905 now
emits the pending payload as its own event rather than silently replacing
it. Also remove a quarantine ternary whose null branch was unreachable.
Every store for the same canonical directory now shares one process-wide
lock, listing no longer deletes files, and only saving rotates recent
history. Saved history no longer borrows the recent-history limit; both
stores share one path-safe directory helper, one atomic write utility,
and one log-time formatter.
Keep the open batch in memory behind an atomically reserved .partial name
and write the deduplicated roster once at protocol completion, so
SaveResult.rowCount always matches the published file.
Replace the two copies of the bounded CSV tokenizer and the history
envelope split with single implementations, and collapse the repeated
decoder field loops into inline helpers that keep zero-allocation
skipping of unknown fields.
Move every screen onto ComponentActivity with system-bar and keyboard
insets, and add the shared top bar, screen shell, background work,
document access, label, dialog, and state-view building blocks used by
the redesigned screens.
Replace stringly-typed capture messages with CapturePhase, localized
CaptureNotice entries, and one immutable CaptureSnapshot swapped by CAS,
so UI and service updates and guided-payload marks can no longer tear
or lose writes. Status and pending-Platoon candidates now notify
main-looper listeners; read() and readGuidedProgress() remain as
deprecated delegates until the UI migrates. The pending store keeps a
running payload total instead of summing on every offer.
CaptureVpnService is now main-thread lifecycle glue. One service-lifetime
CaptureWorker owns all flow, parser, session, and admission state; native
chunks share its bounded budget while teardown and admission tasks are
never rejected. Teardown joins the native thread, drains queued chunks,
and clears state on that worker, and a start requested meanwhile waits
for it. Pending-Platoon confirm and discard therefore also work after
capture stopped.

Flow routing moves to CaptureFlowEngine, admission to
PlatoonAdmissionCoordinator, the notification to CaptureNotifications,
counters to CaptureTrafficStats, and the one-time checklist to
CaptureRunTracker. Package UIDs are resolved once per session, an
unchanged 21905 no longer rewrites the profile registry, the duplicate
routing paths and PROFILE checklist marks are merged, and the stale
'not integrated yet' message is replaced.
Manual weekly edits are now merged with stored overrides by a tested
policy that reports the invalid field instead of a generic failure.
- Platoon: virtualized roster with search, Active/Withdrawn/All filter,
  sort chooser, summary tiles, and long-press selection for CSV export.
- Member detail: stats card, profile fields that survive recreation and
  are no longer wiped by history edits, and labelled membership periods.
- Member order: drag handles with move up/down accessibility actions and
  confirmed reset; snapshot comparison moved off the main thread.
- All repository work runs through lifecycle-bound background work.
- Sticky day header and frozen member column with synced horizontal
  scrolling in one virtualized list; 12sp+ cells instead of 8-11sp.
- Tapping a cell explains every metric of that day in one sheet; edit
  mode edits one day at a time with tri-state Login/Patrol choices,
  highlighted changes, cancel with discard confirmation, and drafts
  that survive recreation.
- Notes are deleted only through an explicit delete action; week
  navigator, evidence summary, and a live-table action for restored
  history; exports regenerate content instead of holding it in memory.
- History preview renders the native table instead of a bitmap, restore
  cannot be submitted twice, and cutline settings validate per field.
- Shared weekly PNGs use localized evidence text and the app palette.
- Home observes typed capture status through listeners instead of
  polling English text: localized notices, a one-time checklist, client
  install state, and one Stop action while busy. Notification permission
  is requested when capture starts instead of at launch.
- Packet history uses Recent/Saved tabs with long-press selection,
  confirmed deletion, and off-main-thread loading.
- Roster CSV import moves into its own flow with a structured preview.
- Settings groups Platoon, display, capture, backup, Discord, and about;
  both backup kinds and restore live there, and diagnostics open as a
  copyable sheet. The welcome guide can be replayed.
- New-Platoon admission prompts follow the pending store directly, keep
  the chosen server across recreation, and close when resolved.
- Packet detail toggles between table and raw text in both directions.
- Remove the legacy tree-walking styler and toast helpers.
A roster captured exactly at Sunday 05:00 closes the previous week, and a
current roster or membership event is the carried-in baseline of later
weeks, but only the capture's own game week was re-recorded. Weekly
history refresh now also re-records those listed neighbours within the
per-ingest bound.
…egion

A complete restore wrote the archived effective region as an explicit
override, and a later Platoon-only restore could change the profile
region while that override kept the old zone. Restores now restore an
archived profile region as automatic, clear the override when a
Platoon-only restore changes the region, and journal the exact
timezone keys so rollback no longer turns automatic into explicit.
Custom nicknames and private, membership-period, and weekly notes had no
length limit below the input fields. Manual edits now trim and reject a
name over 256 characters or a note over 1,000 characters before any row
is written.
The restore journal rejected a region or zone string that the timezone
reader tolerates, so an interrupted restore with such a key could never
finish recovering. The journal now replays the raw keys unchanged.
Cleanup deleted the settings snapshot before the PREPARED state, and
recovery then required the missing snapshot, so a process death there
failed recovery on every launch. Cleanup now deletes the state first and
recovery treats a missing snapshot as already rolled back.
Editing Login to no over a stored Daily Patrol yes, or the reverse,
produced an attended=false, dailyPatrol=true override that
replaceWeeklyOverrides refuses, failing the whole save. The edited
choice now settles the contradiction, and contradictory choices in one
edit are reported as invalid.
The share projection cut member labels at 128 and private notes at 512
UTF-16 units, which could leave half of a surrogate pair and render a
broken glyph. Shortening now stops before a split pair.
…checks

The consistency policy dated snapshot and exact boundaries in UTC while
manual dates are game-zone calendar dates, and compared instants when a
date was missing. A manual stay before a same-day exact join, or a
date-only rejoin on the day of an exact withdrawal, was then rejected as
an overlap. Missing dates are now derived in the Platoon's game zone.
The history codec never wrote a cell's manual override, so a restored
revision lost its MANUAL precision, Clear action, and edited values,
and an edit started from it saw no existing overrides. Revisions with
overrides now use history format 3 and carry them; revisions without
overrides keep format 2 bytes and fingerprints.
Settings reads passed a target package copied unvalidated from the
v2.2 store straight to the complete-backup codec, which rejected it, so
complete export and restore failed. Reads now fall back to the default
package exactly as the legacy migration already did.
seal released the scope claim in its own finally, so after a failed
seal runImport rolled the import back unclaimed and then released the
claim again, possibly clearing another operation's claim. runImport now
holds one claim across apply, seal, and rollback and releases it once.
Drops the unreferenced PlatoonActivityFact and DailyActivity models, the
unused MemberRow.observedDays, MemberRow.hasIncompleteEvidence, and
DayCell.isGunsmokeActivityUnknown properties, a correlation sort branch
excluded by its own WHERE clause, and an always-false solver condition,
and reuses ActivityInference.counterDelta in snapshot comparison.
…ivity

A roster that joins or withdraws a member can date the boundary to an
activity time after the previous roster, in an earlier week, but only
the roster's own week was re-recorded. The refresh now also covers the
times of the membership events the roster recorded.
… fields

- Weekly saves merge with the stored overrides, so a restored table or an off-roster member keeps its values
- Nickname and note inputs use the persistence limits; contradictory login/patrol choices get their own message
- Follow the weekly PNG state into its export flow in the recreation test
@1window2
1window2 merged commit 82390bd into main Oct 15, 2026
5 checks passed
@1window2
1window2 deleted the v2.5.0 branch October 16, 2026 05:07
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.

2 participants