Files
eng-pad/PROGRESS.md
Kyle Isom ba1571011a Fix critical bugs found in code audit
- MoveStrokesAction.undo(): was offsetting by (0,0), now restores
  original pre-move point data correctly
- Stroke commit flicker: new strokes render incrementally onto existing
  backing bitmap instead of triggering full redraw
- Selection state leak: view-side selection cleared on page navigation
  via onPageNavigated callback
- Initial page fallback: blank canvas prevented if initialPageId
  not found in DB — falls back to first page
- Arrow head Paint: preallocated reusable object, no per-call allocation
- Updated PROGRESS.md with audit findings and backup design notes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:35:06 -07:00

186 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROGRESS.md — eng-pad Implementation Progress
This file tracks completed work and decisions. Updated after every step.
See PROJECT_PLAN.md for the full step list.
## Completed
### Phase 0: Project Documents (2026-03-24)
- [x] 0.1: Created DESIGN.md — full technical design covering architecture,
data model, coordinate system, rendering, input handling, and source tree.
- [x] 0.2: Created PROJECT_PLAN.md — discrete steps grouped by phase with
checkboxes and file references.
- [x] 0.3: Created PROGRESS.md — this file.
- [x] 0.4: Updated CLAUDE.md — build commands, source tree, project doc
pointers.
### Phase 1: Project Skeleton + Data Layer (2026-03-24)
- [x] 1.1: Generated Android project — Gradle 8.14.2, AGP 8.10.1, Kotlin 2.1.20
- [x] 1.2: Version catalog with Compose BOM 2026.03.00, Room 2.8.4,
Navigation 2.9.7, Lifecycle 2.10.0
- [x] 1.3: Lint configured — warningsAsErrors, AGP version check suppressed
(AGP 9.x needs Gradle 9.x)
- [x] 1.4: Room entities: Notebook, Page, Stroke, PageSize enum
- [x] 1.5: Converters: FloatArray ↔ ByteArray (packed little-endian)
- [x] 1.6: DAOs: NotebookDao, PageDao, StrokeDao
- [x] 1.7: EngPadDatabase (Room, version 1)
- [x] 1.8: NotebookRepository, PageRepository
- [x] 1.9: Unit tests: StrokeBlobTest (6 tests), PageSizeTest (4 tests) — all pass
- Foojay resolver added for automatic JDK toolchain download
- compileSdk/targetSdk bumped to 36 (required by latest androidx dependencies)
### Phase 2: Notebook List Screen (2026-03-24)
- [x] 2.1: MainActivity with Compose NavHost (three routes: notebooks, pages, editor)
- [x] 2.2: NotebookListViewModel with StateFlow, create/delete operations
- [x] 2.3: NotebookListScreen — lazy list, create dialog (title + page size radio),
long-press delete with confirmation, empty state
- [x] 2.4: Auto-create page 1 in NotebookRepository.create()
- [x] 2.5: Navigation wired — tap notebook → pages stub, editor stub
- EngPadTheme: high-contrast light color scheme (black on white for e-ink)
### Phase 3: Canvas — Basic Drawing (2026-03-24)
- [x] 3.13.4: PadCanvasView — stylus input with historical points, Path/Paint
stroke rendering, direct path drawing (no backing bitmap), 60pt grid
drawn in screen space with pixel-snapped positions, Matrix coordinate
transform (canonical ↔ screen)
- [x] 3.5: CanvasState — tool enum (PEN_FINE, PEN_MEDIUM, ERASER, SELECT, BOX),
zoom/pan state, pen width constants (4.49pt, 5.91pt)
- [x] 3.6: EditorViewModel — loads strokes from Room, saves on completion
- [x] 3.7: EditorScreen + Toolbar — Compose wrapper with AndroidView,
FilterChip toolbar for pen size and eraser selection
- [x] 3.8: NavGraph updated — pages route auto-navigates to first page's editor,
page size passed through route params
- Used KTX Canvas extensions (withMatrix, withScale, createBitmap) per lint
- ClickableViewAccessibility suppressed on PadCanvasView (drawing view)
### Phase 4: Zoom and Pan (2026-03-24)
- [x] 4.1: ScaleGestureDetector with focal-point zoom (dynamic min4×)
- [x] 4.2: Finger drag for pan with multi-pointer tracking, pan clamped
so page always fills viewport
- [x] 4.3: Input routing by tool type already in place from Phase 3
- Zoom/pan state managed locally in PadCanvasView for responsiveness,
synced to ViewModel on gesture end
- Minimum zoom computed dynamically from page/view dimensions
### Phase 5: Eraser (2026-03-24)
- [x] 5.15.3: Stroke-level eraser with bounding box hit test (42pt radius),
processes historical touch points for thorough erasing, deletes from
view + Room DB
### Phase 6: Undo/Redo (2026-03-24)
- [x] 6.1: UndoableAction interface, AddStrokeAction, DeleteStrokeAction
- [x] 6.2: UndoManager with undo/redo stacks (depth 50), StateFlow for canUndo/canRedo
- [x] 6.3: EditorViewModel rewired — stroke add/delete go through UndoManager,
visual callbacks sync canvas view without full reload
- [x] 6.4: 9 unit tests for UndoManager (perform, undo, redo, depth limit, clear, no-ops)
- Toolbar now has undo/redo buttons with enabled state
### Phase 7: Selection — Move, Copy, Delete (2026-03-24)
- [x] 7.17.2: Rectangle selection with dashed rect and blue highlight
- [x] 7.3: Delete, drag-to-move, copy operations with toolbar buttons
- [x] 7.4: Full undo integration — DeleteMultipleStrokesAction, MoveStrokesAction,
CopyStrokesAction
- Drag existing selection to move, tap outside to deselect
### Phase 8: Multi-Page Navigation (2026-03-24)
- [x] 8.1: PageListScreen — adaptive grid of page cards with correct aspect ratio
- [x] 8.2: Add new page via FAB
- [x] 8.3: NavGraph refactored — pages route loads notebook metadata then shows
PageListScreen, tap page navigates to editor with page size
### Phase 9: PDF Export (2026-03-24)
- [x] 9.1: PdfExporter — creates PdfDocument, scales canonical 300 DPI coords
to 72 DPI PDF points (×0.24), renders strokes without grid
- [x] 9.2: Share via Intent.ACTION_SEND + FileProvider (configured in Phase 1)
- [x] 9.3: PDF button in editor toolbar, exports current page
### Phase 10: Polish (2026-03-24)
- [x] 10.1: High-contrast e-ink theme — expanded color scheme with proper
container, variant, and outline colors for readability
- [x] 10.2: Auto-save already in place since Phase 3 (strokes save on completion)
- [x] 10.3: Empty states and delete confirmations already in place from Phase 2
- [ ] 10.4: Performance profiling — requires on-device testing
### On-Device Testing — Daylight DC-1 (2026-03-24)
Tested on DC-1, identified and fixed rendering issues:
- **Grid**: original grid drawn in canonical space had uneven spacing due to
sub-pixel positioning when scaled. Fixed by drawing grid in screen space
with pixel-snapped line positions. Now uniform squares.
- **Stroke quality**: original backing bitmap at 1/4 resolution caused blurry
strokes. Fixed by removing backing bitmap entirely and drawing paths directly.
Anti-aliasing disabled on all paint for crisp e-ink lines.
- **Viewport**: dark gray background was visible around page edges. Fixed by
clamping pan to page edges, computing dynamic min zoom, and white background.
- **Box tool**: works correctly on device.
- **PDF export**: correctly elides grid — confirmed on device.
- **Line snap**: increased threshold to 60pt (~5mm) and switched to max-distance
tracking to handle EMR stylus hand tremor.
### Code Audit & Bug Fixes (2026-03-24)
Critical/high-priority fixes applied:
- **MoveStrokesAction.undo()**: was offsetting by (0,0) instead of restoring
original positions. Fixed to write back the pre-move point data.
- **Stroke commit flicker**: backing bitmap was fully redrawn on each new
stroke. Fixed with incremental rendering — new strokes draw directly
onto the existing bitmap without clearing.
- **Selection state leak**: view-side selection wasn't cleared on page
navigation. Added `onPageNavigated` callback.
- **Initial page fallback**: if `initialPageId` isn't found in DB,
fall back to first page instead of showing blank canvas.
- **Arrow head Paint allocation**: preallocated reusable Paint object
instead of allocating per drawArrowHeads() call.
Known issues still to address:
- CopyStrokesAction uses `takeLast(n)` to find new stroke IDs — fragile
- No mutex guarding concurrent page navigation + paste operations
- Grid redrawn every frame (could be cached)
## Backup Design (Not Yet Implemented)
Notebook backup as a zip file:
- Export: `notebook_title.engpad.zip` containing:
- `notebook.json` — notebook metadata (title, page_size, page count)
- `pages/` directory with one JSON per page:
- `pages/001.json` — page metadata + strokes array
- Each stroke: `{penSize, color, style, points: [x0,y0,x1,y1,...]}`
- Points stored as JSON float arrays (portable, human-readable)
- Import: parse zip, create notebook + pages + strokes in DB
- Could be shared via Android share intents like PDF/JPG export
- Consider: export all notebooks as a single zip for full backup,
or individual notebook export for sharing
Alternative: SQLite `VACUUM INTO` for raw DB backup (simpler but not
portable or human-readable).
## Status
All implementation phases complete. Remaining work:
- Test on Supernote Manta
- Verify line snap behavior on device
- Performance profiling with heavily annotated pages
- Implement notebook backup/restore
## Decisions & Deviations
- **Language**: Kotlin (chosen over Java for better Compose/coroutine support).
- **Storage**: Room/SQLite with packed float BLOBs for stroke points.
- **Palm rejection**: Hardware only (EMR digitizer handles it).
- **Pressure sensitivity**: None — four pen sizes (0.38, 0.5, 0.6, 0.7mm).
- **Coordinate system**: 300 DPI canonical points (scaled to 72 DPI for PDF export).
- **UI framework**: Compose for chrome, custom View for canvas (Compose
Canvas lacks MotionEvent access needed for stylus input).