Files
zemion db873063f8
Verify source and build Android / android (push) Canceled after 0s
initial commit v 0.2.0
2026-09-24 22:49:27 +02:00

9.3 KiB

Lightweight file browser — 0.2.0

This is implemented source, not a device-verified APK. See VALIDATION.md for the executed checks.

Everyday workflow

Open Browse → Add folder, select an allowed folder in Android's system UI, and grant read access. The folder becomes a saved location when the provider offers a persistable grant. Tap it to browse its children. Folder rows navigate; file rows open the existing read-only preview. Files / Back returns to the browser with its location, filter, and saved Compose scroll state. Up and the breadcrumb buttons navigate visited folders. Breadcrumbs represent the visited route, not a guessed filesystem path. An incoming nested directory link can have a direct root-to-target breadcrumb.

Use Open file for a single file anywhere the system picker can access, including individual Downloads items. That system UI does not require opening Google Docs or Microsoft Office. A provider may need its own network/account; Fileworkbench itself has neither an account nor network permission.

The Recent tab contains opened previews. Removing a recent item or clearing history does not revoke saved folder grants. Removing a saved folder releases its tree read grant and does not delete files. Removing a folder can make recent documents relying on that grant inaccessible.

Browser controls

  • Saved locations, nested folder navigation, breadcrumbs, Up, refresh, and explicit access errors.
  • Literal, case-insensitive filename search inside the loaded current folder only.
  • Sort by name, modified date, size, or MIME type. Folders stay first; unknown sizes/dates stay last.
  • Dotfile visibility toggle. No recursive content indexing or background storage crawl.
  • File type badges, provider-supplied size/date, and file-details dialog.
  • More → Preview uses the existing PDF/image/text/Office/CSV/ZIP preview path.
  • More → Open in another app hands off a read-only copy using a chooser that excludes this viewer. This also works for unsupported preview formats when their bytes fit the copy limit.
  • Opt-in provider thumbnails. Off by default; enable them in a folder's Options menu. Only visible rows whose provider advertises thumbnail support are requested. Other files retain type badges. No automatic full-file PDF/Office rendering is performed while listing a folder.

Office support remains text/table reading previews, not full document-layout rendering. Formats unsupported by the existing viewer have not gained rendering engines merely by appearing in a list.

Registration as a files application

The main manifest declares:

Entry point Behaviour
MAIN + LAUNCHER Normal application entry.
MAIN + android.intent.category.APP_FILES Android's files-app launch category, introduced in API 29. Opens browser locations.
VIEW + content-directory MIME types Opens a usable granted document tree; otherwise explains how to select a folder.
Existing file VIEW / single-file SEND Existing file-preview entry points remain present.
GET_CONTENT + OPENABLE + */* Separate FilePickerActivity, used by apps asking a third-party picker for file contents.

APP_FILES is not the web-browser role. Registration makes the app an eligible handler; it does not guarantee that an OEM or caller exposes a default-files-app switch or chooser.

ACTION_OPEN_DOCUMENT and ACTION_OPEN_DOCUMENT_TREE remain system-owned. No activity claims those actions. Fileworkbench is not a general DocumentsProvider re-exporting the user's saved folders. The debug variant alone has a protected synthetic-fixture DocumentsProvider for instrumentation; it contains no user's files and is excluded from release builds.

Acting as another app's file picker

The incoming request's base MIME filter and optional EXTRA_MIME_TYPES are checked; extra filters narrow the base filter. Invalid requests fail closed. Folders remain navigable but cannot be selected. Virtual documents cannot be returned as ordinary byte streams; export them in their provider first.

Single selection replaces the previous file; multi-selection is allowed only when requested. Selections can span folders and be reviewed/removed. Nothing is returned automatically: the user must press Use file(s). Previewing is available through a row's More menu; the preview footer can select/deselect that file. Cancelling returns RESULT_CANCELED and no file grant.

For EXTRA_LOCAL_ONLY, saved provider folders are deliberately not browsed: this app cannot prove that an arbitrary provider's documents are already offline. Open file delegates to the system picker with LOCAL_ONLY and the caller's MIME/multiple constraints. Provider/platform conformance to that hint still matters; this is not a hard network sandbox around other installed providers.

At confirmation the app re-queries the selected documents and validates their types/capabilities. Actual bytes are copied with per-file and aggregate limits. Results contain fresh narrow FileProvider URIs, with only FLAG_GRANT_READ_URI_PERMISSION. Multi-results use ClipData, and a single result also has a data URI/type. No original tree URI, prefix, write, or persistable grant is returned. MIME metadata is preserved for copied extensionless files.

These are temporary imported copies, not in-place edit handles or permanent cloud links. Copies may expire after a day, be evicted by Android, or be removed by explicit cache clearing. Receivers should import/read them promptly. Browser external-open copies have the same limits and semantics.

Exported activities sanitize intent extras before ViewModel construction. External callers cannot seed internal SavedStateHandle keys such as sourceUri or browser.selection to silently preselect files from previously granted storage. Only the expected standard request parameters survive.

Bounds and trade-offs

Resource Current bound
Saved folders 24
Navigation depth 64 visited levels; repeated-folder loops rejected
Listing 5,000 visited provider rows; truncation is explicitly shown
Search Current loaded directory; query capped at 256 characters
Selection 1 file, or up to 16 when multiple selection is requested
Preview / picker / external-open copy 64 MiB per file
Picker request aggregate 128 MiB
Picker-copy disk cache 256 MiB and 32 sessions; new copies refused when full
Thumbnails 96-pixel requested size, 4 MiB memory cache, two concurrent requests
Provider metadata/thumbnail queries Bounded worker pool and 20-second awaiting timeout with CancellationSignal

A truncated directory is not a complete device search: sort/filter only affect the loaded subset. Use the system picker to reach other entries. Refresh is manual, including when a cloud provider reports that more entries are still loading. There is no claim of live watching or recursive search.

Blocking provider streams and thumbnail/native decoders still need device-level cancellation, resource-pressure, and hostile-provider testing. A timeout can stop the UI waiting; it cannot force an arbitrary external provider to stop executing. See the remaining work in SECURITY.md.

Storage that is deliberately unavailable

There is no all-files-access permission. Android 11+ restricts granting the internal-storage root, reliable SD-card roots, and the Downloads root via a directory grant; Android/data and Android/obb also have restrictions. Other apps' private storage is not accessible. A permitted Downloads subfolder can be bookmarked where offered; individual Downloads files use Open file.

There are no rename/delete/move/create-folder controls, broad MediaStore gallery index, raw-path browser, SMB/WebDAV/SFTP client, or archive extraction operations in this release.

Reuse on iOS

core/commonMain/browser contains real shared entry/location/listing models, DirectoryReader, filename filters/sorting, MIME validation, selection policy, safe labels, and streamed-copy budgets. IDs remain opaque strings. Android ContentResolver, Uri grants, thumbnails, FileProvider, and Compose remain in androidApp. The future iOS host supplies security-scoped URL/bookmark access and a native browser/preview UI; Android picker registration is not an iOS feature. No Apple binary was built here.

Folder-listing correction in 0.2.2

Versions 0.2.0 and 0.2.1 can reject a valid folder with "The storage provider did not return a folder listing." A nullable use result, caused by the absence of an optional provider error message, was incorrectly treated as a missing cursor. 0.2.2 moves the null check to the query itself. This is not a request for wider permissions. Rebuild/install the fix and reopen or refresh the saved folder. See HOTFIX-0.2.2.md and VALIDATION.md for the change and verification boundary.

SAF-D1 diagnostics integrated in 0.2.3

The continuing user-reported listing failure is unresolved. This release keeps the 0.2.2 null-check correction and adds a distinct [SAF-D1/NULL_CURSOR] error only when the query itself returns no cursor. It reports the provider authority, Android API and retained-read-grant status, not file paths or contents. The Locations footer includes the app version and Browser diagnostics SAF-D1 so the installed source can be identified. Query behaviour and permissions remain unchanged. See RELEASE-0.2.3.md and BUILD.md before reproducing the failure.