# Navigation

All routes and user flows in Scanly **v1.0.17**.

Navigation is implemented with **Navigation Compose** in `ScanlyNavHost.kt`. Route helpers follow the `*Destination` object pattern with `routePattern` and `route()` factory functions.

## Navigation shell

| Form factor | Chrome |
| --- | --- |
| Phone | `Scaffold` + bottom `NavigationBar` |
| Tablet | Destination-owned `NavigationRail` (92 dp) with app logo on top-level screens |

- **Start destination:** `home`
- **Tab switches:** no transition animation
- **Detail pushes:** short fade with a small forward offset; back navigation reverses the motion
- **Large-screen stability:** the tablet rail is composed inside each top-level destination, so entering or leaving a detail route never resizes the `NavHost` mid-transition
- **Top inset:** each screen applies status-bar padding once (not doubled by the activity shell)

## Top-level tabs

| Route | Screen | In bottom nav / rail |
| --- | --- | --- |
| `home` | Home dashboard | Yes |
| `library` | Full library | Yes |
| `tools` | Tools hub (Scan, PDF, Utilities) | Yes |
| `settings` | Settings | Yes |

## Legacy placeholder routes

These top-level routes still exist but show `FeaturePlaceholderScreen` — they are sprint-era stubs, not active flows:

| Route | Note |
| --- | --- |
| `camera` | Use `camera/session/{documentId}` instead |
| `review` | Review happens in document detail and page preview |
| `editor` | Use `editor/page/{pageId}` instead |

## Typed routes (active flows)

| Route pattern | Helper object | Screen |
| --- | --- | --- |
| `document/{documentId}` | `DocumentDestination` | Document detail |
| `library/search` | `LibrarySearchDestination` | Library name search across documents and folders; owns separate query state |
| `camera/session/{documentId}?replacePageId={pageId}` | `ScanSessionDestination` | Scan session |
| `preview/page/{pageId}` | `PageImagePreviewDestination` | Page preview |
| `editor/page/{pageId}` | `PageEditorDestination` | Page editor (preview, filters/adjust overlays, retake, delete) |
| `crop/page/{pageId}` | `PageCropDestination` | AI detect, rotate, four-point crop, reset, apply |

**Editor overlays (not NavHost routes):** `FilterPickerScreen` and `FilterCustomizeScreen` share `PageEditorViewModel` and replace the editor content in place (same pattern as a full-screen mode, not a bottom sheet).
| `group/{groupId}` | `GroupDetailDestination` | Group detail |
| `legal/{documentType}` | `LegalDocumentDestination` | Privacy or terms viewer |
| `settings/appearance` | `SettingsAppearanceDestination` | Theme + pure black |
| `settings/detection` | `SettingsDetectionDestination` | Models, gate, benchmark link |
| `settings/widgets` | `SettingsWidgetsDestination` | Pin home-screen widgets |
| `settings/about` | `SettingsAboutDestination` | Version, updates, legal, support |
| `settings/faq` | `SettingsFaqDestination` | FAQ sub-screen |
| `settings/licenses` | `SettingsLicensesDestination` | Open-source licenses |
| `settings/storage` | `SettingsStorageDestination` | Storage & backup |
| `settings/model-benchmark` | `SettingsModelBenchmarkDestination` | Temporary local model comparison |
| `tools/qr` | `ToolsQrDestination` | QR scan, typed result, create, and history (in-route screens) |
| `tools/pdf/reader?filePath={filePath}&fileName={fileName}` | `ToolsPdfReaderDestination` | PDF reader; optional app-owned result file opens directly |
| `tools/pdf/merge` | `ToolsPdfMergeDestination` | PDF merge |
| `tools/pdf/compress` | `ToolsPdfCompressDestination` | PDF compress |
| `tools/pdf/password` | `ToolsPdfPasswordDestination` | PDF password protect/remove |
| `tools/pdf/watermark` | `ToolsPdfWatermarkDestination` | PDF text watermark |

### Back behavior

- When opened from Tools, Back from a PDF reader returns to the file picker; Back from that picker exits to Tools. A PDF preview opened from a PDF tool returns to that tool's result screen. **Choose another** opens a temporary picker, where Back returns to the current reader.
- In PDF compression, Back from options returns to the file picker; Back from that picker exits to Tools. **Change** opens a temporary picker, where Back returns to options.
- In watermarking, Back from the editor returns to the picker; **Change** opens a temporary picker, where Back returns to the current editor.
- Android Back closes the editor's full-screen filter and adjustment modes before leaving the editor. It is blocked while a page is being captured, cropped/saved, filter-saved, or processed by a PDF tool.

### Scan session arguments

- `documentId` (required) — target document
- `replacePageId` (optional) — when set, capture replaces this page instead of adding a new one. On complete, navigates to `editor/page/{replacePageId}`.

### Settings sub-screen ViewModel sharing

Settings sub-screens that edit preferences (`appearance`, `detection`, `widgets`, `about`, `faq`, `licenses`, `storage`) share `SettingsViewModel` via the parent `settings` back stack entry. The model benchmark owns a separate ViewModel because its runs and results are temporary.

## External launch actions (widgets & shortcuts)

`MainActivity` accepts explicit launch actions from widgets and launcher shortcuts (`singleTop`). `LaunchActionViewModel` queues the action until onboarding is complete, then redirects:

| Action | Intent action | Redirect |
| --- | --- | --- |
| Scan | `in.c1ph3rj.scanly.action.SCAN` | Suggest title → create document → `camera/session/{documentId}` |
| Import | `in.c1ph3rj.scanly.action.IMPORT` | System photo picker → import pipeline → `document/{documentId}` |
| QR | `in.c1ph3rj.scanly.action.QR` | Tools tab → `tools/qr?mode=scan` |
| Library | `in.c1ph3rj.scanly.action.LIBRARY` | Top-level `library` |

Config: `AndroidManifest.xml` receivers + `res/xml/shortcuts.xml`; providers live under `feature/widget/`.

### Widget / shortcut → scan

```
Widget or quick action (Scan)
  └─► MainActivity (action.SCAN)
        └─► (after onboarding) suggest title + create document
              └─► camera/session/{newDocId}
```

## User flow diagrams

## User flow diagrams

### App startup

```
Launch
  └─► Onboarding required?
        ├─ Yes → OnboardingScreen → complete → Home
        └─ No  → Home
              └─► (background) update check if cooldown expired
```

### Create and scan

```
Home / Library
  └─► Create + Scan (default name prefilled; clear or Suggest name)
        └─► camera/session/{newDocId}
              └─► capture page(s)
                    └─► document/{docId}
```

### Edit a page

```
document/{docId}
  └─► preview/page/{pageId}
        ├─► overflow: Share / Edit / Retake / Delete
        └─► editor/page/{pageId}
              ├─► Filters  → full-screen FilterPickerScreen (live preview + presets; Done → editor)
              ├─► Adjust   → full-screen FilterCustomizeScreen (sliders + compare; Done → editor)
              ├─► crop/page/{pageId}
              │     ├─► AI Detect / rotate / handles / Reset
              │     └─► Done → reprocess crop+rotation → navigateUp → editor
              ├─► editor Done → reprocess filter + adjustments → back to preview/detail
              └─► retake → camera/session/{docId}?replacePageId={pageId}
                              └─► editor/page/{pageId}  (replacement complete)
```

### Gallery import

```
Home (new doc) or document/{docId} (add pages)
  └─► photo picker (≤10 images)
        └─► import pipeline (same as capture finalize)
              └─► document/{docId}
```

### Group workflow

```
Library (Folders filter) or Home (recent groups)
  └─► group/{groupId}
        ├─► document/{docId}  (open member)
        ├─► rename / delete group
        ├─► add/remove documents
        └─► export merged PDF or zipped PDFs
```

### Export

```
document/{docId} or group/{groupId}
  └─► export sheet (PDF options: password, page numbers, orientation, size, margins)
        ├─► Save → configured Downloads/SAF destination (unique filenames)
        └─► Share → cache/exports → FileProvider share sheet
```

### Library backup and restore

```
settings → settings/storage
  ├─► Back up library → WorkManager foreground job → {destination}/backup/*.scanly
  └─► Restore → pick .scanly in backup folder → Replace or Merge as copies
```

### Settings

```
settings
  ├─► Look & feel: theme mode + pure black (AMOLED) toggle
  ├─► settings/storage (usage, destination, backup/restore, clear data)
  ├─► Document detection: automatic or manual live/post models
  ├─► Document detection: physical-document gate on/off
  ├─► settings/model-benchmark (gate + four corner models on local images)
  ├─► settings/faq
  ├─► legal/{PRIVACY} or legal/{TERMS}
  ├─► settings/licenses
  └─► check for updates → AppUpdateDialog (channel-specific)
```

## Onboarding gate

`MainActivity` uses `AnimatedContent` to switch between onboarding states:

| State | UI |
| --- | --- |
| `LOADING` | Blank / loading |
| `REQUIRED` | `OnboardingScreen` |
| `COMPLETE` | `ScanlyNavHost` |

Automatic update checks run on `ON_START` only when onboarding is `COMPLETE`.

## Related docs

- [screens.md](screens.md) — screen and ViewModel mapping
- [../overview/user-guide.md](../overview/user-guide.md) — user-facing walkthrough
- [overview.md](overview.md) — architecture layers
