daf19c34b33a72eb9acb3eef4570f4ece4056c73
P1 - Tools screen no longer exposes dead 'coming with the feature build'
cards. Replaced the stale Calibration and Units/Feedback cards with a
single working Settings card that opens the Settings screen.
P1 - Detailed (4-point) calibration now validates rotation quality, not
just the final mean. A 4-point set holds two independent 180-flip bias
estimates - the (0,180) and (90,270) pairs; a malformed turn makes them
diverge even when the mean stays small (which the 3-degree magnitude
guard can't see). isRotationConsistent rejects divergence beyond 0.4
degrees. The disagreement scales with the surface's true tilt, so it's
lenient near level and strict on a steeper permitted surface. Test
proves a deliberately 10-degree-over-rotated set is rejected and a clean
set passes.
P2 - Calibration persistence moved from the screen's rememberCoroutineScope
to a container-owned applicationScope, so the write survives an immediate
Done/back after 'saved'. Preference writes moved there too.
Cleanup: haptic copy is mode-neutral ('when the level locks'); Settings
no longer keeps the screen awake (calibration still does); calibration
method renamed Thoroughness -> Method with Standard/Detailed labels and
accurate 'Four positions, a quarter-turn apart' / 'Averages more sensor
noise' copy; lime now marks the active-calibration status (confirmed
state), consistent with lime = level/confirmed.
64 tests passing; assembleDebug clean; verified on device.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On the Level
Offline-first Android pocket toolset: a trustworthy level, angle meter, and calibrated screen ruler (Pro). Kotlin + Jetpack Compose, single module, no backend.
- BRIEF.md — the authoritative v1 product & technical brief
- AUDIT.md — audit of the brief; the amendments it lists are incorporated
- resources/ — concept board (visual direction)
Building
Open in Android Studio, or:
./gradlew assembleDebug # build
./gradlew testDebugUnitTest # measurement-math unit tests
Scaffold status
The project skeleton follows BRIEF.md §Sensor and measurement architecture. Implemented and tested:
core/sensors— sensor selection (game rotation vector → gravity → low-passed accelerometer), device-frame gravity stream, and the pure measurement math: orientation mapping, two-sample per-mode calibration (derived in angle space, applied in vector space so it stays correct away from zero), EMA smoothing, lock hysteresis/dwell/debounce, display deadband, placement validity, and the sensor-vector contract (all sources emit device-frame world-up; flat = +9.81 on Z, pinned bySensorContractTest). All unit-tested.core/settings— DataStore preferences and per-mode calibration persistence.core/billing— entitlement interface with a stub (free-tier) implementation.feature/level,feature/angle— live numeric scaffolds wired to the real sensor pipeline, with manual Surface|Edge selection, placement-validity hints, lock state (locked label states the tolerance so it can never contradict the rounded readout), and directional hold-to-zero (vector reference, not magnitude subtraction).feature/ruler,feature/tools— static placeholders.
Not yet implemented (deliberately — see TODO markers):
- The tactile vial visuals and the spring-animated display value (value 3 of 3 in the brief); scaffold screens render the deadbanded stable value directly.
- Guided calibration flow UI (math and persistence are done).
- Edge-mode readout counter-rotation on the portrait-locked activity.
- Play Billing implementation of
ProEntitlementRepository. - Screen ruler measurement UI and per-display scale persistence.
- Audio cue, reduced-motion behaviors, preferences UI.
Description
Languages
Kotlin
83.9%
HTML
16.1%