Skip to content

Sprint 1A: Vision foundation and Vision Lab - #2

Draft
xeriomy wants to merge 49 commits into
mainfrom
sprint-1-vision-foundation
Draft

xeriomy wants to merge 49 commits into
mainfrom
sprint-1-vision-foundation

Conversation

@xeriomy

@xeriomy xeriomy commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

Sprint 1A of DraftLens: the image-bearing foundation for vision work, plus the Sprint 1A.1
Vision Lab interaction redesign and its corrections.

  • Non-null frame contract — FramePayload/OriginalFrame/ProcessedFrame/SlotImage.
  • Rotation-aware geometry — FrameTransform with source/oriented/crop/processed round trips
    for all four rotations, including 180°/270° with crop and downscale.
  • Validated preprocessing — honest failure codes, no partial frames.
  • Manual slot annotations — stable ids, bounds/duplicate/overlap validation, strict JSON codec.
  • Versioned calibration profiles — 1 % aspect tolerance, exact orientation match,
    unsupported-schema refusal, delete-as-escape-hatch.
  • Vision Lab — a second Material 3 tab with a frame preview, freeze/resume, explicit
    processing, an interactive annotation canvas, a slot list and profile controls.

Not implemented (deliberately)

No OCR, no hero recognition, no automatic draft/ban/pick detection, no ML model, no network
client, no screenshot persistence, no FLAG_SECURE. Annotations are manual calibration data,
never detections. The Vision Lab shows "Detection: not implemented".

Sprint 1A.1 — annotation interaction redesign

The frame sizing and rendering fix is preserved. The user confirmed it working on a
physical device in portrait and landscape before this work started; the oriented bitmap
rendering, the image/overlay/normalized-coordinate mapping, the aspect-ratio behaviour, the
letterbox rejection and the responsive workspace were left as they are.

The gesture model is explicit. AnnotationInteraction is a pure state machine, so two
gestures can never compete and every rule is unit-tested:

Interaction Action
Short tap on a rectangle Select that slot
Drag inside a selected rectangle Move the selected slot
Long press on a rectangle Open its contextual property editor
Drag a resize handle Resize the selected slot
Long press on empty image space, then drag Draw a new slot
Short tap on empty image space Nothing
Tap the X control Delete the selected slot

A tap never moves anything. A drag on an unselected rectangle selects it and stops there, so
a drag can never move the wrong slot. A stationary long press is not a move. A drag on empty
space never creates a rectangle — creating requires the long press first.

The delete control replaced the top-right resize handle. The top-right handle is gone
and the X sits on exactly that corner, so the corner carries one control and not two. The
remaining handles — top-left, bottom-left, bottom-right — still change the width and the
height from either side.

The long press opens the menu immediately. It is detected on its own timeout rather than
waiting for a pointer event, so the menu no longer needs the finger to move first, and a
small movement afterwards neither delays nor repeats it.

The contextual property editor replaces the bottom controls. A long press opens a compact
editor next to the slot with BAN/PICK, ALLY/ENEMY/GLOBAL, the index and an optional label,
plus Copy, Paste and Duplicate. It positions itself inside the window, so it is never clipped
off-screen in either orientation, and it no longer occupies a permanent part of the canvas.
The old bottom property controls and the "Add slot" card with its hardcoded rectangle are
gone.

Copy, paste and duplicate use the application's own clipboard, never the Android system
clipboard. A pasted slot gets a new unique id, is offset a little, stays inside the image
and takes the next free index in its (type, team) scope; the source is always preserved.

Deletion and undo. Undo covers a deletion, a creation and a move or resize, and restores
the correct slot id and metadata.

The page no longer scrolls during annotation. The canvas takes the pointer from the first
touch, so moving, resizing and drawing a slot never scrolls the page. Scrolling stays
available everywhere else, and the fullscreen canvas is not inside a scrolling parent.

Fullscreen editing was simplified. Pinch-to-zoom, pan, Fit to screen and Reset zoom
were removed together with the view-transform state they needed. The entry is a compact
expand icon, the editor fills the screen with the aspect ratio preserved and the overlay
aligned, a compact collapse icon leaves it, and Android Back leaves the editor before the
Vision Lab. The orientation is never forced.

The slot list is grouped into ALLY · BAN, ENEMY · BAN, ALLY · PICK, ENEMY · PICK and
GLOBAL, with every row keyed by key(slot.id). Tapping a row selects that rectangle on the
canvas and selecting a rectangle highlights its row. The store was already deleting by id;
the real risk was Compose list identity, which the stable keys remove, and a regression test
deletes the second of three slots and checks the others survive.

Slot colours. ALLY is blue, ENEMY is red, GLOBAL is neutral grey, and every rectangle
also carries a readable label, so colour is never the only cue. There is no fixed number of
bans or picks.

Tests

./gradlew test — 258 unit tests, 0 failures, 0 errors, 0 skipped (run in both the debug
and release variants: 516 test executions).

Also in CI: assembleAndroidTest, strict lint (abortOnError = true) and assembleDebug,
with the draftlens-debug APK uploaded as a workflow artifact and the packaged manifest
re-inspected for the foreground-service permissions and the mediaProjection service type.

Latest green run (branch head): https://github.com/xeriomy/DraftLens/actions/runs/43515376361
Head SHA e1aca691e169a9109f972fb4ffd0d25e14cc8727
Artifact draftlens-debug, 10 213 689 bytes
Artifact digest sha256:82ca55b26978b23408866a338181047fe84b7612ad39b2cd7596f646a7b2cfe1
APK SHA-256 447c6664c4dc72ba1a45aebe2dcfe94751d26366096fcd8153df3904cc4e477a

Instrumentation tests are compiled, not executed — no device or emulator was used here.

Limitations

  • The authoring host is aarch64 and AGP 8.6.1 ships only an x86_64 aapt2, so no Gradle task
    that touches resources can run locally. CI is the build of record.
  • Physical-device verification is pending for the 1A.1 work. The frame sizing and
    rendering fix was confirmed by the user on a device; the gesture model, the delete control,
    the long press, the contextual editor, the undo, the slot list and the fullscreen editor
    were not.
  • The canvas is a scroll barrier, so the page is scrolled by dragging on the cards around it,
    not on the canvas itself.
  • Applying a profile regenerates slot ids (the store owns id generation).
  • No performance or 24/7 stability claim; durations are recorded per run in the UI.

Status

Draft — not merged.

root and others added 30 commits November 6, 2026 01:44
… cancel on clear and validate calibration slots
…ess on its own timeout and simplify fullscreen editing
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.

1 participant