Files
eng-pad/PROGRESS.md
Kyle Isom 6e5c500786 Update project docs to reflect current state after post-Phase 10 polish
- PROGRESS.md: remove stale known issues (all fixed), add post-Phase 10
  feature polish section covering toolbar redesign, 4 pen sizes, line/box/move
  tools, edge swipe nav, page reorder, notebook rename, filter/sort, JPG
  export, clipboard ops, startup state restoration, and DB migrations
- PROJECT_PLAN.md: add Phase 11 (Server Sync Integration) and Phase 12
  (Notebook Backup/Export) with step breakdowns
- DESIGN.md: add Tools table, sync architecture section, backup/export design,
  JPG export, stroke styles, startup state restoration, edge swipe nav; update
  rendering strategy (3-layer compositing), source tree, schema, and pen sizes
- CLAUDE.md: update build commands, architecture, source tree, and key
  conventions to match current codebase

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

12 KiB

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)

  • 0.1: Created DESIGN.md — full technical design covering architecture, data model, coordinate system, rendering, input handling, and source tree.
  • 0.2: Created PROJECT_PLAN.md — discrete steps grouped by phase with checkboxes and file references.
  • 0.3: Created PROGRESS.md — this file.
  • 0.4: Updated CLAUDE.md — build commands, source tree, project doc pointers.

Phase 1: Project Skeleton + Data Layer (2026-03-24)

  • 1.1: Generated Android project — Gradle 8.14.2, AGP 8.10.1, Kotlin 2.1.20
  • 1.2: Version catalog with Compose BOM 2026.03.00, Room 2.8.4, Navigation 2.9.7, Lifecycle 2.10.0
  • 1.3: Lint configured — warningsAsErrors, AGP version check suppressed (AGP 9.x needs Gradle 9.x)
  • 1.4: Room entities: Notebook, Page, Stroke, PageSize enum
  • 1.5: Converters: FloatArray <-> ByteArray (packed little-endian)
  • 1.6: DAOs: NotebookDao, PageDao, StrokeDao
  • 1.7: EngPadDatabase (Room, version 1)
  • 1.8: NotebookRepository, PageRepository
  • 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)

  • 2.1: MainActivity with Compose NavHost (three routes: notebooks, pages, editor)
  • 2.2: NotebookListViewModel with StateFlow, create/delete operations
  • 2.3: NotebookListScreen — lazy list, create dialog (title + page size radio), long-press delete with confirmation, empty state
  • 2.4: Auto-create page 1 in NotebookRepository.create()
  • 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)

  • 3.1-3.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)
  • 3.5: CanvasState — tool enum (PEN, LINE, BOX, ERASER, SELECT, MOVE), zoom/pan state, PenSize enum with 4 sizes
  • 3.6: EditorViewModel — loads strokes from Room, saves on completion
  • 3.7: EditorScreen + Toolbar — Compose wrapper with AndroidView, icon-button toolbar with long-press menus for pen size and line style
  • 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)

  • 4.1: ScaleGestureDetector with focal-point zoom (dynamic min-4x)
  • 4.2: Finger drag for pan with multi-pointer tracking, pan clamped so page always fills viewport
  • 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)

  • 5.1-5.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)

  • 6.1: UndoableAction interface, AddStrokeAction, DeleteStrokeAction
  • 6.2: UndoManager with undo/redo stacks (depth 50), StateFlow for canUndo/canRedo
  • 6.3: EditorViewModel rewired — stroke add/delete go through UndoManager, visual callbacks sync canvas view without full reload
  • 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)

  • 7.1-7.2: Rectangle selection with dashed rect and blue highlight
  • 7.3: Delete, drag-to-move, copy operations with toolbar buttons
  • 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)

  • 8.1: PageListScreen — adaptive grid of page cards with correct aspect ratio
  • 8.2: Add new page via FAB
  • 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)

  • 9.1: PdfExporter — creates PdfDocument, scales canonical 300 DPI coords to 72 DPI PDF points (x0.24), renders strokes without grid
  • 9.2: Share via Intent.ACTION_SEND + FileProvider (configured in Phase 1)
  • 9.3: PDF button in editor toolbar, exports current page

Phase 10: Polish (2026-03-24)

  • 10.1: High-contrast e-ink theme — expanded color scheme with proper container, variant, and outline colors for readability
  • 10.2: Auto-save already in place since Phase 3 (strokes save on completion)
  • 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 switching to a screen-resolution backing bitmap with incremental stroke addition. 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.
  • CopyStrokesAction: fixed takeLast(n) fragile ID lookup — now tracks copied stroke IDs directly.
  • Grid caching: grid bitmap is now cached and only redrawn on zoom/pan/resize changes.

Post-Phase 10: Feature Polish (2026-03-24)

Extensive feature work after the initial 10-phase plan:

  • Toolbar redesign: replaced FilterChip toolbar with icon-button toolbar. Custom Canvas-drawn icons for each tool (pen dot, line, box outline, eraser, dashed-rect select, four-way-arrow move, curved undo/redo arrows).
  • Pen size consolidation: expanded from 2 sizes to 4 sizes (0.38mm, 0.5mm, 0.6mm, 0.7mm). Single PEN tool with long-press for size selection dropdown; remembers last size.
  • Line tool: dedicated LINE tool (separate from PEN) with style variants via long-press menu: plain, arrow, double-arrow, dashed. Line style stored in stroke style column in DB.
  • Box tool: draws rectangles corner-to-corner. Uses current pen size and style.
  • Move tool: dedicated MOVE tool for tap-and-drag stroke relocation (separate from select-then-drag).
  • Edge swipe navigation: finger swipe from left/right screen edge navigates to previous/next page. Auto-creates new page on forward swipe past last page (if current page has strokes). Edge zone = 8% of screen width, minimum 100px horizontal distance.
  • Page list drag-to-reorder: long-press drag in PageListScreen to reorder pages. Uses longPressDraggableHandle with reorderable lazy column. Page numbers renumbered sequentially on drop.
  • Notebook rename: long-press notebook in library list to rename. Rename dialog with text field.
  • Library filter/sort: search field to filter notebooks by title, sort by last-edited or title (ascending/descending).
  • JPG export: single-page JPEG export at 300 DPI via Export dropdown menu. Uses Bitmap.compress(JPEG, 95), shared via FileProvider + intents.
  • Clipboard operations: cut, copy, paste toolbar buttons appear when strokes are selected or clipboard is non-empty. Cut = copy + delete.
  • Startup state restoration: SharedPreferences in EngPadApp store last opened notebook ID and view-all-pages flag. On launch, auto-navigates to last notebook (or page list if that was the last screen).
  • Page navigation from editor: binder button in toolbar shows "p1/5"-style page indicator with dropdown for "View all pages" and "Go to page..." dialog.
  • Last page tracking: Notebook entity has lastPageId column; editor resumes at last-visited page when reopening a notebook.
  • Page delete: delete pages from PageListScreen with sequential renumbering of remaining pages.
  • Database migrations: schema at version 3 (added style column to strokes, last_page_id to notebooks).

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. Post-phase polish significantly extended the feature set beyond the original plan. Remaining work:

  • Test on Supernote Manta
  • Performance profiling with heavily annotated pages
  • Implement notebook backup/restore (zip format)
  • Server sync integration (eng-pad-server, gRPC, manual sync)

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).
  • Toolbar: Icon buttons with Canvas-drawn icons (no drawable resources needed for tool icons). Long-press for sub-menus (pen size, line style).
  • Rendering: Screen-resolution backing bitmap with incremental stroke addition + cached grid bitmap. Both invalidated on zoom/pan/resize.
  • Line styles: Stored as string in stroke style DB column (plain, dashed, arrow, double_arrow) rather than a separate table.
  • State restoration: SharedPreferences for last notebook/page rather than Compose SavedStateHandle (survives process death).