diff --git a/SURFACE_LEVEL_PLAN.md b/SURFACE_LEVEL_PLAN.md new file mode 100644 index 0000000..19327db --- /dev/null +++ b/SURFACE_LEVEL_PLAN.md @@ -0,0 +1,134 @@ +# Surface Level — Implementation Attack Plan + +**Status:** Proposed; send to Claude for audit before implementation. +**Scope:** Surface Level only. Do not change Angle, Edge, ruler, or billing in this stage. + +## Outcome + +Build a trustworthy two-axis surface-level tool for a phone lying flat, +screen-up, on a surface. It must feel like a physical bullseye vial while +giving clearer, more actionable information than a physical level. + +The user should be able to answer three questions at a glance or by sound: + +1. Is this surface level? +2. How far off is it? +3. Which side/corner needs adjustment, and by roughly how much? + +## Existing foundation to preserve + +- Unified device-frame world-up sensor contract. +- Raw sensor -> stable measurement -> animated display separation. +- Vector-space Surface calibration model and two-sample calibration math. +- EMA smoothing, lock hysteresis/dwell/debounce, deadband, and current tests. +- The current numeric Surface reading, pitch/roll values, and haptic lock hook. + +## Product decisions to implement + +- Surface means the phone is intended to lie flat, screen-up, on the plane being + measured. Its level-lock behavior is valid only in that geometry. +- Numeric measurements are never hidden because the phone is steeply oriented. + Outside the ideal Surface geometry, show the actual angle and a quiet placement + hint; suppress level-lock feedback rather than replacing the reading with `—`. +- The central visualization is a bullseye vial. It has a finite visual range: + beyond that range the bubble rests at the rim, while numeric information remains + full-range and authoritative. +- Calibration corrects device/setup bias. It is not "make this desk level." + A later relative-reference feature is separate and out of scope here. + +## Work sequence + +### 1. Make measurement behavior honest at every angle + +- Refactor Surface placement gating so it only controls the level-lock state, + lock haptic, and geometry-specific guidance. +- Keep primary angle, pitch, and roll visible at 0-90 degrees. +- Add tests at flat, near level, 30 degrees, and near vertical proving numeric + readings remain present and correct; verify invalid placement never locks. +- Keep the existing lock tolerance semantics: a lock label states the tolerance + rather than pretending any non-zero value is exactly flat. + +### 2. Add the guided Surface calibration flow + +- Add a Surface Calibration route from the Level header. +- Explain that the phone may be calibrated on any firm, stable surface; the + surface itself does not need to be level. +- Step 1: settle, sample for a short stable window, capture. +- Step 2: rotate the phone exactly 180 degrees in the same plane, settle, capture. +- Reject/retry if readings indicate motion or inconsistent placement. +- Persist only the resulting Surface calibration; never alter Edge calibration. +- State that the calibration applies to the current phone/case/contact setup. +- On completion, show a concise success result and a way to clear/re-run it. + +### 3. Build the tactile bullseye instrument + +- Use Compose Canvas (or an equivalent composable drawing layer) for a scalable + circular vial, etched rings, crosshairs, target zone, and bubble. +- Glass, fluid, and meniscus effects must improve depth/readability; avoid a + decorative or excessively glossy treatment. +- Bubble target position derives directly from stable calibrated pitch/roll. +- Bubble movement uses a critically damped animation toward that real target: + no random drift, autonomous wobble, or animation that contradicts the number. +- Respect disabled system animations and the app's reduced-motion preference by + minimizing or removing the display spring. +- Show a large total tilt beside/below the vial, then Pitch and Roll below it. +- At level lock, use the restrained lime state and an explicit text label. + +### 4. Add actionable adjustment information + +- Derive a screen-relative high/low direction from calibrated Pitch and Roll. + Use language such as `High: lower-left`; do not claim a compass direction. +- Display slope as both degrees and a useful normalized rise/run value: + millimetres per metre and/or inches per foot based on the selected unit system. +- Treat exact board/counter dimensions and calculated shim/foot height as a later + Adjustment Assist feature, not a requirement of this stage. +- When the reading is moving, show a subtle `Settling` state and defer strong + correction guidance until the stable measurement is trustworthy. + +### 5. Add optional hands-free Audio Level Assist + +- Surface-only, off by default, explicitly user-enabled. +- Use a soft sonar-like pulse with a gentle attack and naturally fading tail; + never a harsh alarm or rapid smoke-detector cadence. +- Silent while settling or materially outside the usable placement geometry. +- Near level: occasional low-key proximity pulses. +- Locked: one distinct soft lock ping; optionally provide a sparse repeat-while- + locked preference so a user adjusting a large plank can hear that it remains level. +- Reuse the existing lock dwell/hysteresis/debounce so sound cannot chatter. +- Respect media volume, audio focus, system sound settings, app lifecycle, and + the user's Audio Assist preference. + +## Surface acceptance criteria + +- A stable phone on a known level plane achieves lock without repeated feedback. +- A 180-degree calibration on a stable but non-level plane improves repeatability + without zeroing that plane's real slope. +- A screen-up phone at 30 and near 90 degrees still shows meaningful numbers; + it does not falsely claim Surface lock. +- Bubble direction agrees with Pitch/Roll and with the high/low instruction. +- Bubble visual remains physically believable but never leads or disagrees with + the stable numeric reading. +- Audio can guide a user to lock without viewing the screen and stays quiet when + disabled, settling, backgrounded, or outside the Surface geometry. +- Manual checks cover phone-with-case, phone-without-case, a firm tabletop, and + a large board adjusted by one person. + +## Explicitly deferred + +- Angle-screen redesign and signed relative-angle semantics. +- Edge/Plumb visual and geometry changes. +- Saved measurements, target-angle alerts, screen ruler, and Play Billing. +- Board dimension/shim calculator and automatic flat/edge switching. + +## Questions for Claude's audit + +1. Does the relaxed placement gate preserve truthful full-range Surface numbers + while preventing false lock feedback? +2. Is the two-sample calibration flow mathematically and UX-wise safe for the + stated use case, including phone cases and non-level calibration surfaces? +3. Is high/low mapping unambiguous and testable for all pitch/roll quadrants? +4. Does the bullseye animation remain derived from measurement truth and respect + reduced-motion/system-animation settings? +5. Can the audio state machine reuse lock semantics without audio-focus, + lifecycle, or repeated-feedback problems? +6. Is any proposed work outside this single-stage scope? diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 0000000..6c1139e --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,12 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect +toolchainVersion=21