Add project documentation: design spec, implementation plan, and progress tracking

Initial project setup with README, CLAUDE.md (AI dev context), DESIGN.md
(full technical design covering architecture, data model, coordinate system,
rendering strategy), PROJECT_PLAN.md (phased implementation steps), and
PROGRESS.md (completion tracking).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 13:42:44 -07:00
commit 47778222b7
5 changed files with 593 additions and 0 deletions

82
CLAUDE.md Normal file
View File

@@ -0,0 +1,82 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
eng-pad is an Android note-taking app built around notebooks with an EMR pen as the primary input method. Target devices are the Supernote Manta (Android 11) and Daylight DC-1 (Android 13). Minimum API level is Android 11 (API 30).
## Build Commands
```bash
./gradlew build # Compile everything
./gradlew test # Run unit tests
./gradlew lint # Run Android Lint
./gradlew installDebug # Install debug APK to connected device
# Run a single test class:
./gradlew test --tests "net.metacircular.engpad.data.StrokeBlobTest"
```
## Architecture
- **Kotlin + Jetpack Compose** for UI chrome (screens, toolbar, dialogs)
- **Custom View** (`PadCanvasView`) for the drawing canvas — Compose Canvas lacks `MotionEvent` access needed for stylus input
- **Room** (SQLite) for persistence — notebooks, pages, strokes
- **Single Activity** with Compose Navigation (three screens: notebook list → page list → editor)
- **Coordinate system**: 300 DPI canonical points. Regular page = 2550×3300pt, large = 3300×5100pt. Scaled to 72 DPI (×0.24) for PDF export.
- **Input dispatch**: `TOOL_TYPE_STYLUS` → draw/erase/select, `TOOL_TYPE_FINGER` → zoom/pan
## Project Documents
- **DESIGN.md** — Full technical design (architecture, data model, rendering, source tree)
- **PROJECT_PLAN.md** — All implementation steps with checkboxes. Check off steps as completed.
- **PROGRESS.md** — What's been done, what's in progress, decisions made. Update after every step.
**Keep PROJECT_PLAN.md and PROGRESS.md in sync.** When a step is completed, check it off in PROJECT_PLAN.md and log it in PROGRESS.md. When files are added, update the source tree in both DESIGN.md and this file.
## Source Tree
```
eng-pad/
├── app/
│ ├── build.gradle.kts -- Module build config
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin/net/metacircular/engpad/
│ │ │ ├── EngPadApp.kt -- Application class
│ │ │ ├── MainActivity.kt -- Single activity, Compose NavHost
│ │ │ ├── data/
│ │ │ │ ├── db/ -- Room database, DAOs, converters
│ │ │ │ ├── model/ -- Entities: Notebook, Page, Stroke, PageSize
│ │ │ │ └── repository/ -- NotebookRepository, PageRepository
│ │ │ ├── ui/
│ │ │ │ ├── navigation/NavGraph.kt -- Route definitions
│ │ │ │ ├── notebooks/ -- List screen + ViewModel
│ │ │ │ ├── pages/ -- Page grid screen + ViewModel
│ │ │ │ ├── editor/ -- PadCanvasView, EditorScreen, ViewModel
│ │ │ │ ├── export/PdfExporter.kt -- PDF generation + sharing
│ │ │ │ └── theme/Theme.kt -- Material3 theme
│ │ │ └── undo/ -- UndoManager, UndoableAction
│ │ └── res/
│ └── test/ -- Unit tests
├── build.gradle.kts -- Root build config
├── settings.gradle.kts
├── gradle.properties
├── gradle/libs.versions.toml -- Version catalog
├── CLAUDE.md -- This file
├── README.md
├── DESIGN.md -- Technical design
├── PROJECT_PLAN.md -- Implementation steps
└── PROGRESS.md -- Completion tracking
```
## Key Conventions
- Stroke points are packed as little-endian float BLOBs: `[x0,y0,x1,y1,...]`
- All coordinates are in canonical space (300 DPI). Screen transform via `Matrix`.
- Grid spacing: 60pt (~5 squares/inch). Grid is drawn on screen, excluded from exports.
- Pen widths: 0.38mm = 4.49pt, 0.5mm = 5.91pt (at 300 DPI).
- Two pen sizes only, no pressure sensitivity.
- Hardware palm rejection only (EMR digitizer).

334
DESIGN.md Normal file
View File

@@ -0,0 +1,334 @@
# DESIGN.md — eng-pad Technical Design
## Overview
eng-pad is an Android note-taking app for EMR pen devices. It provides a
notebook-based writing surface with a guide grid, two fixed pen sizes, and
PDF export. Target devices are the Supernote Manta (Android 11) and the
Daylight DC-1 (Android 13).
## Architecture
**Kotlin + Jetpack Compose + Custom Canvas View.**
The app uses a single-Activity architecture with Jetpack Compose for UI
chrome (notebook list, page list, toolbar, dialogs) and Compose Navigation
for screen transitions. The drawing surface is a custom `View` subclass
(`PadCanvasView`) hosted inside Compose via `AndroidView` — Compose's
`Canvas` composable does not provide direct access to `MotionEvent` dispatch
or hardware-accelerated `Path` rendering, both of which are critical for
low-latency stylus input.
### Screen Flow
```
NotebookListScreen → PageListScreen → EditorScreen
| | |
Create/delete Page grid, PadCanvasView +
notebooks add pages Toolbar (Compose)
```
Three Compose navigation destinations:
| Route | Screen | Purpose |
|------------------------|--------------------|--------------------------------|
| `notebooks` | NotebookListScreen | List, create, delete notebooks |
| `pages/{notebookId}` | PageListScreen | Page thumbnails, add pages |
| `editor/{pageId}` | EditorScreen | Drawing canvas + toolbar |
### Dependency Stack
| Layer | Technology | Purpose |
|--------------|-----------------------------|---------------------------------|
| UI | Jetpack Compose + Material3 | Screens, toolbar, dialogs |
| Drawing | Custom View + Canvas API | Stroke rendering, input |
| State | ViewModel + StateFlow | Reactive UI state |
| Persistence | Room (SQLite) | Notebooks, pages, strokes |
| Async | Kotlin Coroutines | Background DB operations |
| Navigation | Compose Navigation | Screen routing |
| Export | Android PdfDocument API | PDF generation |
| Share | FileProvider + Intents | PDF sharing (Dropbox, etc.) |
No external drawing libraries. The Android `Canvas` + `Path` + `Paint` API
is sufficient and avoids dependencies that may behave unpredictably on e-ink
displays.
## Coordinate System
All stroke data is stored in **canonical coordinates at 300 points per inch**
for high precision with fine pen strokes. Coordinates are scaled to 72 DPI
during PDF export (multiply by 72/300 = 0.24).
| Page Size | Inches | Canonical Points |
|-----------|----------|-------------------|
| Regular | 8.5 × 11 | 2550 × 3300 |
| Large | 11 × 17 | 3300 × 5100 |
### Grid
The guide grid uses ~5 squares per inch = 300/5 = **60 points** per grid
square. The grid is drawn as thin gray lines on screen but excluded from PDF
export and any other output.
### Pen Sizes
Two fixed stroke widths, corresponding to Muji gel ink ballpoints:
| Pen | Millimeters | Canonical Points |
|-------|-------------|------------------|
| Fine | 0.38 mm | 4.49 pt |
| Medium| 0.50 mm | 5.91 pt |
No pressure sensitivity — stroke width is uniform for a given pen size.
### Screen Transform
A `Matrix` maps canonical coordinates to screen pixels. The matrix encodes
the current zoom level and pan offset. All stylus input coordinates are
transformed through the inverse matrix before being stored, ensuring strokes
are always in canonical space regardless of zoom.
```
canonical → [viewMatrix] → screen pixels
screen pixels → [inverseMatrix] → canonical
```
Zoom range: 0.5× to 4×. Pan is clamped so the page cannot scroll entirely
off-screen.
## Data Model
### SQLite Schema (Room)
```sql
CREATE TABLE notebooks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
page_size TEXT NOT NULL CHECK(page_size IN ('regular', 'large')),
created_at INTEGER NOT NULL, -- epoch millis
updated_at INTEGER NOT NULL -- epoch millis
);
CREATE TABLE pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
notebook_id INTEGER NOT NULL REFERENCES notebooks(id) ON DELETE CASCADE,
page_number INTEGER NOT NULL,
created_at INTEGER NOT NULL,
UNIQUE(notebook_id, page_number)
);
CREATE TABLE strokes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,
pen_size REAL NOT NULL, -- width in canonical points
color INTEGER NOT NULL, -- ARGB packed int
point_data BLOB NOT NULL, -- packed floats: [x0,y0,x1,y1,...]
stroke_order INTEGER NOT NULL, -- z-order within the page
created_at INTEGER NOT NULL
);
CREATE INDEX idx_strokes_page ON strokes(page_id);
CREATE INDEX idx_pages_notebook ON pages(notebook_id);
```
### Stroke Point Encoding
Points are stored as a `BLOB` of packed little-endian floats:
`[x0, y0, x1, y1, x2, y2, ...]`. A stroke with N points uses N × 2 × 4
bytes. A typical stroke of 200 points = 1600 bytes. This is ~10× more
compact than JSON and eliminates parsing overhead.
```kotlin
// Encode
fun FloatArray.toBlob(): ByteArray {
val buf = ByteBuffer.allocate(size * 4).order(ByteOrder.LITTLE_ENDIAN)
for (f in this) buf.putFloat(f)
return buf.array()
}
// Decode
fun ByteArray.toFloatArray(): FloatArray {
val buf = ByteBuffer.wrap(this).order(ByteOrder.LITTLE_ENDIAN)
return FloatArray(size / 4) { buf.getFloat() }
}
```
## Input Handling
### Tool Type Dispatch
The EMR digitizer reports stylus events as `TOOL_TYPE_STYLUS` and finger
touches as `TOOL_TYPE_FINGER`. This provides a clean input split without
software palm rejection:
| Tool Type | Action |
|------------------|-------------------------------------|
| `TOOL_TYPE_STYLUS` | Draw, erase, or select (per mode) |
| `TOOL_TYPE_FINGER` | Pinch-to-zoom, pan |
### Historical Points
The Wacom EMR digitizer batches events. To capture all intermediate points
for smooth strokes, `PadCanvasView` processes `MotionEvent.getHistoricalX/Y`
on every `ACTION_MOVE`, not just the current event coordinates.
### Modes
The editor toolbar controls the active mode:
| Mode | Stylus Behavior |
|----------|------------------------------------------------------|
| Draw | Create strokes with the active pen size |
| Erase | Touch a stroke to delete it (stroke-level eraser) |
| Select | Draw a rectangle to select strokes, then move/copy/delete |
## Rendering Strategy
### On-Screen
1. **Completed strokes** are rendered to a backing `Bitmap`. This bitmap is
redrawn only when strokes change (add, delete, move).
2. **In-progress stroke** (pen is down) is drawn directly to the canvas on
each `onDraw` call, on top of the backing bitmap.
3. **Grid** is drawn as a separate pass — thin gray lines at 14.4pt intervals.
4. The view `Matrix` transforms everything from canonical to screen space.
This approach means `onDraw` is fast for the common case (pen moving):
draw the cached bitmap + one active path + grid.
### PDF Export
Uses Android's `PdfDocument` API. `PdfDocument` pages use 1/72-inch units,
so stroke coordinates are scaled by 72/300 = 0.24 during export. A `Matrix`
pre-concatenated with the export canvas handles this. The grid drawing pass
is skipped.
Export flow:
1. Create `PdfDocument`.
2. For each page: add a `PdfDocument.Page` with canonical dimensions,
draw all strokes using the same `Path`/`Paint` rendering code.
3. Write to a temp file in the app's cache directory.
4. Share via `Intent.ACTION_SEND` with a `FileProvider` URI.
## Undo/Redo
Command pattern with a depth limit of 50:
| Action | Execute | Undo |
|----------------------|-------------------|--------------------|
| `AddStrokeAction` | Insert stroke | Delete stroke |
| `DeleteStrokeAction` | Delete stroke | Re-insert stroke |
| `MoveStrokesAction` | Offset points | Offset back |
| `DeleteMultipleAction`| Delete strokes | Re-insert strokes |
The `UndoManager` maintains two stacks. Performing a new action clears the
redo stack. Each action also persists or deletes from Room as part of
execute/undo.
## Selection
Rectangle selection (initial implementation; lasso selection as a future
upgrade):
1. In select mode, stylus drag draws a selection rectangle.
2. Strokes whose bounding boxes intersect the rectangle are selected.
3. Selected strokes get a visual highlight (translucent overlay + bounding box).
4. Available operations: delete, drag-to-move, copy/paste.
5. All selection operations are undoable.
## Eraser
Stroke-level eraser (not pixel-level):
1. In erase mode, stylus touch/drag hit-tests against all strokes on the page.
2. Hit test: check stroke bounding box first (fast reject), then check
point-by-point distance (threshold: ~42 canonical points / ~3.5mm).
3. Hit strokes are deleted immediately (with undo support).
4. Backing bitmap is rebuilt after deletion.
## E-ink Considerations
Both target devices have e-ink or e-ink-like displays with high refresh latency:
- Minimize animations and transitions.
- Use high-contrast colors (black on white).
- Minimize invalidation regions (invalidate only the bounding box of new
stroke segments, not the full canvas).
- Consider a manual "refresh" button to force a full-screen redraw to clear
e-ink ghosting.
## Source Tree
```
eng-pad/
├── app/
│ ├── build.gradle.kts -- Module build config
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin/net/metacircular/engpad/
│ │ │ ├── EngPadApp.kt -- Application class
│ │ │ ├── MainActivity.kt -- Single activity, Compose NavHost
│ │ │ ├── data/
│ │ │ │ ├── db/
│ │ │ │ │ ├── EngPadDatabase.kt -- Room database definition
│ │ │ │ │ ├── NotebookDao.kt -- Notebook CRUD
│ │ │ │ │ ├── PageDao.kt -- Page CRUD
│ │ │ │ │ ├── StrokeDao.kt -- Stroke CRUD
│ │ │ │ │ └── Converters.kt -- Room type converters
│ │ │ │ ├── model/
│ │ │ │ │ ├── Notebook.kt -- Room entity
│ │ │ │ │ ├── Page.kt -- Room entity
│ │ │ │ │ ├── Stroke.kt -- Room entity
│ │ │ │ │ └── PageSize.kt -- Enum: REGULAR, LARGE
│ │ │ │ └── repository/
│ │ │ │ ├── NotebookRepository.kt -- Notebook operations
│ │ │ │ └── PageRepository.kt -- Page + stroke operations
│ │ │ ├── ui/
│ │ │ │ ├── navigation/
│ │ │ │ │ └── NavGraph.kt -- Route definitions
│ │ │ │ ├── notebooks/
│ │ │ │ │ ├── NotebookListScreen.kt -- Notebook list UI
│ │ │ │ │ └── NotebookListViewModel.kt -- Notebook list state
│ │ │ │ ├── pages/
│ │ │ │ │ ├── PageListScreen.kt -- Page grid UI
│ │ │ │ │ └── PageListViewModel.kt -- Page list state
│ │ │ │ ├── editor/
│ │ │ │ │ ├── PadCanvasView.kt -- Custom View: rendering + input
│ │ │ │ │ ├── EditorScreen.kt -- Compose wrapper
│ │ │ │ │ ├── EditorViewModel.kt -- Editor state + persistence
│ │ │ │ │ ├── CanvasState.kt -- Zoom, pan, tool, selection
│ │ │ │ │ └── Toolbar.kt -- Editor toolbar
│ │ │ │ ├── export/
│ │ │ │ │ └── PdfExporter.kt -- PDF generation + sharing
│ │ │ │ └── theme/
│ │ │ │ └── Theme.kt -- Material3 theme
│ │ │ └── undo/
│ │ │ ├── UndoManager.kt -- Undo/redo stack
│ │ │ └── UndoableAction.kt -- Action interface + impls
│ │ └── res/
│ │ ├── values/
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ ├── drawable/ -- Toolbar icons
│ │ └── xml/
│ │ └── file_provider_paths.xml -- FileProvider config
│ └── test/
│ └── kotlin/net/metacircular/engpad/
│ ├── data/
│ │ ├── StrokeBlobTest.kt -- Float array ↔ blob roundtrip
│ │ └── RepositoryTest.kt -- CRUD + cascade delete
│ └── undo/
│ └── UndoManagerTest.kt -- Undo/redo logic
├── build.gradle.kts -- Root build config
├── settings.gradle.kts -- Project settings
├── gradle.properties -- Gradle properties
├── gradle/
│ └── libs.versions.toml -- Version catalog
├── .gitignore
├── CLAUDE.md
├── README.md
├── DESIGN.md -- This file
├── PROJECT_PLAN.md -- Implementation steps
└── PROGRESS.md -- Completion tracking
```

30
PROGRESS.md Normal file
View File

@@ -0,0 +1,30 @@
# 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.
## In Progress
Phase 1: Project Skeleton + Data Layer
## 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 — two fixed pen sizes (0.38mm, 0.5mm).
- **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).

120
PROJECT_PLAN.md Normal file
View File

@@ -0,0 +1,120 @@
# PROJECT_PLAN.md — eng-pad Implementation Steps
This file tracks all implementation steps. Check off steps as they are
completed and log them in PROGRESS.md.
## Phase 0: Project Documents
- [x] 0.1: Create DESIGN.md
- [x] 0.2: Create PROJECT_PLAN.md (this file)
- [x] 0.3: Create PROGRESS.md
- [x] 0.4: Update CLAUDE.md with build commands and source tree
## Phase 1: Project Skeleton + Data Layer
- [ ] 1.1: Generate Android project with Gradle
- `build.gradle.kts` (root), `app/build.gradle.kts`, `settings.gradle.kts`
- Kotlin, Compose, Room KSP, minSdk 30, targetSdk 34
- [ ] 1.2: Configure `gradle/libs.versions.toml` version catalog
- Compose BOM, Room, Navigation, Lifecycle, Coroutines
- [ ] 1.3: Configure linting (`app/build.gradle.kts` Android Lint config)
- [ ] 1.4: Define Room entities
- `data/model/Notebook.kt`, `Page.kt`, `Stroke.kt`, `PageSize.kt`
- [ ] 1.5: Implement type converters
- `data/db/Converters.kt``FloatArray``ByteArray`, `PageSize``String`
- [ ] 1.6: Define DAOs
- `data/db/NotebookDao.kt`, `PageDao.kt`, `StrokeDao.kt`
- [ ] 1.7: Define Room database
- `data/db/EngPadDatabase.kt`
- [ ] 1.8: Implement repositories
- `data/repository/NotebookRepository.kt`, `PageRepository.kt`
- [ ] 1.9: Unit tests
- `test/.../data/StrokeBlobTest.kt` — blob roundtrip
- `test/.../data/RepositoryTest.kt` — CRUD, cascade delete
- **Verify:** `./gradlew build && ./gradlew test && ./gradlew lint`
## Phase 2: Notebook List Screen
- [ ] 2.1: Set up `MainActivity` with Compose and `NavHost`
- `MainActivity.kt`, `ui/navigation/NavGraph.kt`, `ui/theme/Theme.kt`
- [ ] 2.2: Implement `NotebookListViewModel`
- `ui/notebooks/NotebookListViewModel.kt`
- [ ] 2.3: Implement `NotebookListScreen`
- `ui/notebooks/NotebookListScreen.kt` — list, create dialog, delete
- [ ] 2.4: Auto-create page 1 on notebook creation
- In `NotebookRepository` or ViewModel
- [ ] 2.5: Navigation: tap notebook → page list (stub screen)
- **Verify:** `./gradlew build && ./gradlew test`
## Phase 3: Canvas — Basic Drawing
- [ ] 3.1: Implement `PadCanvasView` — stylus event handling
- `ui/editor/PadCanvasView.kt``onTouchEvent`, `getHistoricalX/Y`
- [ ] 3.2: Stroke rendering — `Path`/`Paint`, backing bitmap
- [ ] 3.3: Grid drawing — 14.4pt spacing, toggleable
- [ ] 3.4: Coordinate transform — canonical ↔ screen via `Matrix`
- [ ] 3.5: Implement `CanvasState`
- `ui/editor/CanvasState.kt` — zoom, pan, active tool
- [ ] 3.6: Implement `EditorViewModel`
- `ui/editor/EditorViewModel.kt` — load/save strokes from Room
- [ ] 3.7: Implement `EditorScreen` + toolbar
- `ui/editor/EditorScreen.kt`, `ui/editor/Toolbar.kt`
- [ ] 3.8: Wire navigation from notebook/page list to editor
- **Verify:** `./gradlew build && ./gradlew test` + manual on-device test
## Phase 4: Zoom and Pan
- [ ] 4.1: `ScaleGestureDetector` for pinch-to-zoom (0.5×4×)
- [ ] 4.2: Finger drag for pan with bounds clamping
- [ ] 4.3: Input routing — stylus → draw, finger → zoom/pan
- **Verify:** manual on-device test
## Phase 5: Eraser
- [ ] 5.1: Eraser mode in `CanvasState`
- [ ] 5.2: Hit testing — bounding box pre-filter + point distance
- [ ] 5.3: Stroke deletion + backing bitmap rebuild
- **Verify:** `./gradlew test` + manual test
## Phase 6: Undo/Redo
- [ ] 6.1: `UndoableAction` interface + concrete actions
- `undo/UndoableAction.kt`
- [ ] 6.2: `UndoManager` — undo/redo stacks, depth 50
- `undo/UndoManager.kt`
- [ ] 6.3: Integrate with `EditorViewModel` + toolbar
- [ ] 6.4: Unit tests — `test/.../undo/UndoManagerTest.kt`
- **Verify:** `./gradlew test` + manual test
## Phase 7: Selection — Move, Copy, Delete
- [ ] 7.1: Selection mode — rectangle selection
- [ ] 7.2: Visual feedback — highlight, bounding box
- [ ] 7.3: Operations — delete, drag-to-move, copy/paste
- [ ] 7.4: Undo integration for all selection operations
- **Verify:** `./gradlew test` + manual test
## Phase 8: Multi-Page Navigation
- [ ] 8.1: `PageListScreen` with thumbnails
- `ui/pages/PageListScreen.kt`, `PageListViewModel.kt`
- [ ] 8.2: Add new page button
- [ ] 8.3: Prev/next page navigation in editor + auto-save
- **Verify:** manual test
## Phase 9: PDF Export
- [ ] 9.1: `PdfExporter` — Android `PdfDocument` API
- `ui/export/PdfExporter.kt`
- [ ] 9.2: Share via `Intent.ACTION_SEND` + `FileProvider`
- `res/xml/file_provider_paths.xml`, `AndroidManifest.xml` updates
- [ ] 9.3: Export button in toolbar (per-page + whole notebook)
- [ ] 9.4: Unit test — verify PDF generation
- **Verify:** `./gradlew test` + open exported PDF, confirm no grid
## Phase 10: Polish
- [ ] 10.1: E-ink optimizations — minimize animations, high contrast
- [ ] 10.2: Auto-save zoom/pan state per page
- [ ] 10.3: Empty states, error handling, delete confirmations
- [ ] 10.4: Performance profiling on target devices

27
README.md Normal file
View File

@@ -0,0 +1,27 @@
eng-pad
=======
This is an Android app, targeting Android 11, and specifically the
following two devices:
1. The Supernote Manta (Android 11)
2. The Daylight DC-1 (Android 13)
There are a few features it needs to have:
1. It is organized around notebooks.
2. A *regular* notebook's page size is 8.5 x 11 inches. A *large*
notebook's size is 11 x 17 inches.
3. The app features a grid of roughly 5 squares per inch. The grid is
not visible on exported documents.
4. The app should allow pinch to zoom in and out.
5. There should be two pen sizes, corresponding roughly to a Muji gel
ink ballpoint 0.38 or 0.5 pen.
6. It should have the ability to erase, or to select-and-erase.
7. It should have the ability to select and cut/move or copy/paste.
8. The primary method of input is an EMR pen.
9. The app needs to be able to export PDFs though the normal Android
sharing mechanism (e.g. so docs can be exported to Dropbox).
The tech stack can be flexible. The safe default is Java, but if Kotlin
or even Clojure or some other langauge makes more sense, it can be used.