Android · Kotlin · Jetpack Compose · single activity, no DI framework, no repository layer. ~7,900 lines in 8 files: one UI file, one storage facade, one image decoder.
HyperBrowserApp() — no ViewModel; SharedPreferences is the only persistencePane.LEFT / Pane.RIGHT, each with its own root, path stack, sort, selection and list state (BrowserPaneState, saved per pane).
Full-height scrollable toolbar between the panes. TransferDirection drives copy/move only — delete, rename and gallery follow SelectionInfo, the pane that actually holds the selection.
GalleryStage = GRID_SMALL (4 col, 192px) → GRID_MEDIUM (2 col, 640px) → SINGLE. Pinch moves between stages; in SINGLE it zooms, with pan clamped to the overflow the zoom creates.
Every destructive or ambiguous action is a dialog: delete plans with recursive counts, bulk-folder transfer, folder picker, EXIF, "Set image as", layout & sort.
FileEntry — plan first, then execute, so the UI can show what is about to happenResolves direction, selection and destination into an explicit plan, classifies it (files-into-folder, folder-into-folder, bulk), and hands the UI a prompt to confirm before anything moves.
Walks each target recursively and counts files/folders before the delete, so ">1 folder" can raise an itemized "are you sure".
Delete moves into a hidden trash folder beside the original where the provider allows a move; copy/move record what they created. One step of undo. no undo cross-provider
object Storage — the single branch point in the app: if (DriveUris.isDrive(uri)) … else …entry · children · parent · mimeType · openInput · read. Everything above this line sees only FileEntry(uri, name, mimeType, size, …) and never knows which backend it came from.
content:// tree + document URIs via DocumentFile. Seekable, so images decode by region straight off disk. Virtual documents are exported to a concrete MIME type first.
Drive REST v3 behind synthetic gdrive://<fileId> URIs. Not seekable — a file must be downloaded whole before a pixel can be decoded, which is the root of every Drive slowness.
Side channels to the platform: background copied into app storage, center-cropped WallpaperManager.setBitmap, and in-place rotation written as an EXIF tag rather than a re-encode.
A local file is mmap'd (BufferSource), so a 500 MB TIFF is never on the heap. Drive has no equivalent: its bytes arrive as an ArraySource in RAM.
TIFF and camera RAW (ARW, CR2, NEF, DNG, RAF). Parses the TIFF/IFD tree itself; every offset bounds-checked, every allocation capped, since these are untrusted bytes.
Per-vendor sensor decoders (packed, ARW2, Nikon, lossless JPEG) + bilinear demosaic. The inspector's "compressed" toggle instead pulls the camera's largest embedded JPEG.
uri@px.
Because Drive can't seek, ≥200 MB files read in 256 KB chunks through readReporting(), publishing bytes-read on a StateFlow. The single view draws the cached thumbnail with a progress bar over it; Cancel flips a flag the read loop checks between chunks — cancelling the coroutine alone would leave the download running to the end.
Thumbnails sample aggressively (inSampleSize) and are cached, so even a 501 MB TIFF renders one with a short delay. Only the full-resolution single view pays the whole-file cost — which is why the fix is a progress bar, not a thumbnail cap.
All UI, all app state, the gallery and the decode orchestration.
TIFF/RAW container parsing and tiled rendering.
Vendor sensor formats and demosaic.
Backend facade and MIME resolution.
Drive auth, listing, read/export.
Delete plans & trash, EXIF rotation, wallpapers.