422854de07008bee1ef0e15f33204d4be8ddd315
- SurfaceGuidance (core/sensors, pure, tested): the sole source of bubble position, high-side direction, and ring scale. Bubble and 1/2/5-degree etched rings share one 5-degree visual range; the clamped target puts a saturated bubble exactly at the rim ring. - SurfaceBullseye: Compose Canvas glass vial with crosshairs and amber bubble. Critically damped (no-overshoot) spring toward the pre-clamped target; velocity carries across retargets. Reduced motion (in-app pref or ANIMATOR_DURATION_SCALE == 0) snaps directly. - Lock treatment: brief lime ring pulse on acquisition, persistent lime tolerance label while locked, neutral numeric readout. Never color-only; no full-screen lime state. - Pipeline passes stable calibrated pitch/roll to the instrument; deadbanded readout remains authoritative. Bullseye renders only in the FACE_UP presentation. 53 tests passing. Audited-by: Claude (1 finding raised and resolved) 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%