Commit Graph

5 Commits

Author SHA1 Message Date
83d77884a0 Workout screen never moves when a rest starts; a sandbox flavor to prove it on the phone
The rest banner used to be inserted into the column only while a break ran,
so its arrival took height from the exercise list, shifted every tile down
and pushed the last one into scroll range. The strip is now a permanently
reserved 64 dp slot (muted "Rest / 00:00" while idle, Skip keeps its space),
the list is a non-scrolling column that scales down as a whole if it would
not fit (0.96 on the Pixel 6a with four tiles, pinned by a widget test at
the phone's logical size with the status bar simulated), and the activity is
locked to portrait. Verified on the real phone: only the tapped circle's
pixels change when a rest starts.

Verifying that needed a workout screen that today's already-logged workout
did not offer, so the app gained a `sandbox` product flavor beside `daily`:
com.kuhy.workout_app.sandbox installs side by side with its own data, refuses
every network connection at the socket layer, starts no LAN server for the
PC to pull from, keeps its /sdcard files beside the daily build's, wears a
red SANDBOX ribbon, and offers a SANDBOX section in Settings (wipe, mark
today not done, rest length in seconds). MainActivity answers the flavor and
carries a `WorkoutSandbox` logcat trace of every tap, break, save and
navigation. On Linux the same mode is WORKOUT_SANDBOX=1, driven under Xvfb by
scripts/run_sandbox_xvfb.sh. CI builds --flavor daily; .phone-deploy-flavor
tells phone_deploy.sh the same.

Set circles and the home icon buttons carry semantics labels so they can be
addressed by element (and by screen readers).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YbBZqUaNWyhHYfzz6zGC7q
2026-09-20 18:55:08 +02:00
b6e89b93dc workout_app: run the break countdown in a foreground service with notifications
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Tests / test (push) Has been cancelled
Tests / lock-popup-safety (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
The between-set break never finished once the phone's screen was off. Three
independent causes (worked through on-device on 2026-09-12; left
uncommitted awaiting the go-ahead, committed here as-is plus one pin bump):

1. The countdown was not wall-clock: `_breakRemaining--` on a
   Timer.periodic, which Android throttles and Doze suspends in a
   backgrounded isolate, so the counter stalled and _onBreakFinished()
   never fired. Now a BreakClock deadline on the wall clock.
2. An expired break was silently dropped on restore (`if (remaining > 0)`
   only) — now cued and logged.
3. No notification infrastructure at all: no package, no
   POST_NOTIFICATIONS / FOREGROUND_SERVICE / WAKE_LOCK.

Now: flutter_foreground_task runs the countdown in a service isolate that
survives BACK and the screen lock; flutter_local_notifications shows an
ongoing "Workout A · break m:ss / Next: …" notification with −1 rep / Done
actions and a "Break over" alert on its own channel (raw break_end sound,
vibration). Button presses go through a persisted BreakIntent queue the
screen drains on resume, so a press with the app killed is not lost.
Finish and Reset stop the service; a startup reaper handles the crash case.

Verified on the phone with the screen off and locked: service alive and
ticking while Dozing, both notifications on the lock screen, channel
importance 5 with the break_end sound. Not verifiable from adb (receiver is
RECEIVER_NOT_EXPORTED): an actual action-button tap, and a true recents
swipe — see stronglift_replacement/TODO-workout-app-v2.md.

Also fixed en route: the break-end sound had never played in any widget
test (AssetSource needs path_provider, which nothing faked, and the failure
was swallowed by catchError); the queue's prune wrote back a stale list.

This commit additionally:
- takes the freshness gate's bump flutter_local_notifications 22.3.1;
- restores web/package.json to HEAD (the tree held a stale pre-bump copy);
- gives scripts/check_flutter_coverage.sh a FLUTTER_TEST_CONCURRENCY knob:
  the default one-isolate-per-core run peaks over 4 GiB and is OOM-killed
  under the resource cap. Default behaviour unchanged.

Gate: flutter analyze clean, 511 tests, 3187/3187 lines (100%), pre-commit
green. Not re-installed on the phone for the 22.3.1 pin: no device attached
at commit time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7vgacLhb4WJjxuVj2QLEP
2026-09-13 20:28:36 +02:00
d67c770cc9 Latest stable dependencies, and the gate that keeps them there
Python side: gatelock v0.7.1 -> v0.7.2 and crdt-sync v0.9.0 -> v0.9.1 (both
re-cut so consumers stop resolving pre-upgrade constraints), plus every `>=`
floor turned into an exact pin -- five majors among them (mypy 1 -> 2.3.1,
pylint 3 -> 4.0.7, pytest 8 -> 9.1.1, pytest-cov 4 -> 7.1.0, ruff 0.8 ->
0.16.5) and mcp pinned at 2.1.1. `scripts/ci_mirror.sh` builds a clean venv
from requirements.txt and runs the whole CI set: 1146 tests, 100% branch
coverage, no lint or type finding.

workout_app: sync_settings_ui v0.1.0 -> v0.4.1 and ten exact pins, with the
flutter_secure_storage 11 chain (compileSdk 37 / AGP 9.3.2 / Gradle 9.7.1).
411 tests green, analyzer clean, release APK builds.

CI's Flutter pin was 3.44.6 while `.fvmrc` says 3.47.2 -- the drift that made
kuhylog red on a commit that is clean locally. Both workflows now say 3.47.2,
and the gate reads that pin from now on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK
2026-08-28 15:13:11 +02:00
e3cd68dc21 Build and publish a signed APK on every push to main
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
screen-locker had no APK build at all; releases were not produced. Adds a
standalone release-apk.yml plus the release signing config the app was
missing.

Signing follows the pattern proven in home_inventory: gradle reads
android/key.properties when present and falls back to the debug key when
absent, so local `flutter run --release` is unaffected. CI writes that
file from repository secrets and shreds it afterwards, and apksigner
fails the build closed if a debug-signed APK ever appears.

This repo has no Flutter analyze/test job, so the release job is not
gated on tests — a broken commit still publishes, with the build failing
as the only backstop. Add `needs:` if a Flutter CI job is added later.

versionCode is github.run_number, which is per-workflow-file — this file
must never be renamed, or the counter resets and updates stop installing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014dzgcZxmpS3VtmLci2sbxj
2026-08-04 20:55:04 +02:00
26d55fb311 feat: add Flutter workout app (StrongLifts replacement)
Full-featured workout tracker with session persistence, auto A/B cycling,
warmup weights (4/5 of target), settings weight stepper, history + progress
graph, HTTP sync server, and crash-safe active session resume.

Removed per-set break timers per user preference. Dropped audioplayers and
vibration dependencies; updated permission_handler to 12.x to eliminate
two of three KGP build warnings (shared_preferences_android is an upstream issue).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:23:46 +02:00