102 lines
6.2 KiB
Markdown
102 lines
6.2 KiB
Markdown
# Architecture
|
|
|
|
## Boundaries
|
|
|
|
```text
|
|
Android picker / content URI / single-file open-with
|
|
|
|
|
DocumentStore (Android)
|
|
metadata -> size-limited private snapshot -> detector
|
|
|
|
|
+----------------+------------------+
|
|
| | |
|
|
PDF images text / packages
|
|
| | |
|
|
read-only PFD ImageLoader LocalPreviewLoader
|
|
| (Android bitmap) | |
|
|
isolated Service bytes JvmZipArchive
|
|
| | |
|
|
bounded raw ARGB +------+-----+
|
|
| |
|
|
PdfRenderClient common Kotlin core
|
|
| XML / Office / CSV / Markdown
|
|
| models / policy / search / export
|
|
+------------------- Compose UI -------------+
|
|
```
|
|
|
|
Native PDF data travels through granted file descriptors, not provider URIs or filesystem paths.
|
|
The isolated worker cannot browse the app's private files. The host validates dimensions, byte
|
|
length and page metadata before copying raw pixels to a bitmap. There is no PNG decoder in that
|
|
return path. General image decoding still runs in the host process, and common parsers run on an
|
|
IO dispatcher in the host; do not describe all parsing as process-isolated.
|
|
|
|
## Why Kotlin Multiplatform first, not a new C++ format stack
|
|
|
|
The current reusable work is format identification, reading-preview extraction, policy, models,
|
|
search and export. It needs neither Android APIs nor a handwritten native Office rendering engine.
|
|
Those pieces live in `core/src/commonMain`, which also enables direct JVM tests. Kotlin common code
|
|
can be compiled for Apple targets without passing Android URI or Bitmap types through the API.
|
|
|
|
A later LibreOfficeKit or other native renderer can be a separate native adapter. This does not
|
|
require the pure common core or native user interfaces to be rewritten in C++. Conversely, the JVM
|
|
ZIP implementation is explicitly **not** portable to iOS; an equivalent Apple adapter is required.
|
|
|
|
## Common model
|
|
|
|
- `FileClassifier` combines bounded header evidence, name/MIME hints and package entry inspection.
|
|
- `SafetyPolicy` centralizes ceilings and is shared by readers and archive adapters.
|
|
- `ArchiveContainer` exposes metadata and bounded part reads; it must never resolve external URLs.
|
|
- `FormatHandler` is the internal extension seam for package-reading preview handlers.
|
|
- `DocumentPreview` declares its fidelity, section/table/block data, warnings and archive metadata.
|
|
- `PreviewSafety` limits aggregate output amplification, including repeated shared strings.
|
|
- `PreviewText` performs literal searches and a text export; no formula, script or macro execution.
|
|
- `CoreApi` provides a small public boundary useful to a native host.
|
|
|
|
`SafeXml` is a bounded preview-specific XML subset, not a validating standards-complete XML library.
|
|
It rejects DTD/custom entities and verifies basic syntax/nesting. It does not resolve namespace
|
|
URIs, validate schemas, establish signature trust or guarantee complete OOXML/ODF semantics.
|
|
Fuzzing and comparative testing against well-maintained parsers remain release gates.
|
|
|
|
## Android lifecycle
|
|
|
|
`ViewerViewModel` owns the current document, page bitmap, reading preview, recents and loading/error
|
|
state. Rotation retains the ViewModel; saved provider URI/page state allows process-restoration
|
|
attempts. External attachment grants may expire, so reselection is an explicit recovery path.
|
|
|
|
A processing mutex serializes active document operations. New opens cancel earlier requests; input
|
|
copy loops observe coroutine cancellation, and stale staged files are cleaned. Common parsing is
|
|
bounded but not a hard-preemptible sandbox. PDF rendering requests have a timeout; cancellation and
|
|
worker death need further real-device stress testing.
|
|
|
|
Snapshots use random internal names. Display names are sanitized separately. The original provider
|
|
file is never opened for writing. Share exports go into a separate narrow FileProvider directory;
|
|
that provider is not exported. Save/preview-export callbacks check the expected document identity
|
|
so a new incoming document does not silently replace the intended export.
|
|
|
|
## Storage contract
|
|
|
|
The native browser lists user-chosen document trees through `SafBrowserStore`. The system picker
|
|
still supplies user-mediated directory grants and standalone files. There is no whole-device crawl,
|
|
proprietary cloud or direct WebDAV/SFTP client. The app has no network permission; a provider may.
|
|
|
|
`BrowserViewModel` owns locations, visited breadcrumbs, options, selection and provider jobs, separate
|
|
from `ViewerViewModel`. Shared `BrowserEntry` / `BrowserLocation` / `DirectoryReader` types, sorting,
|
|
MIME/selection rules and `SelectionCopyBudget` live in commonMain. Android URI values are opaque IDs,
|
|
not parsed filesystem paths in the core. A saveable Compose state holder retains browser scroll
|
|
when the viewer replaces that screen. Provider metadata work has bounded workers and cancellation.
|
|
|
|
`FilePickerActivity` is a separate standard-launch activity. It accepts GET_CONTENT and returns only
|
|
user-confirmed, bounded snapshots via `WorkbenchFileProvider`, a narrow FileProvider subclass that
|
|
preserves MIME metadata. A single read grant applies to each selected copy, never to a source tree.
|
|
Only the requested standard extras survive activity-intent sanitization before ViewModel creation.
|
|
System OPEN_DOCUMENT is used for standalone selection, preventing GET_CONTENT picker recursion.
|
|
|
|
Folder bookmarks own tree grants. Document-history eviction only releases separate document grants.
|
|
The per-process active-snapshot set protects a live MainActivity preview when a picker viewer opens;
|
|
explicit clear-cache remains an intentional global invalidation of exported copies. Registry/cache
|
|
cleanup and blocked-stream cancellation still require device stress tests.
|
|
|
|
Persistent recents store URI, display name, persistence flag and timestamp, up to 20 entries. No
|
|
content indexing is performed across unopened files. Cache files are plaintext inside the Android
|
|
sandbox; cache deletion is logical deletion, not guaranteed secure erasure.
|