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
NavHostmid-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 documentreplacePageId(optional) — when set, capture replaces this page instead of adding a new one. On complete, navigates toeditor/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 — screen and ViewModel mapping
- ../overview/user-guide.md — user-facing walkthrough
- overview.md — architecture layers