2.4.0 moved Android exports to the system save dialog and typed them
with mime_guess, a table Android does not use. Unknown extensions such
as .jsonl became application/octet-stream, which ColorOS saves as .bin.
Android now passes the extension like HarmonyOS, so the dialog plugin
maps it with MimeTypeMap and leaves unknown extensions untyped.
Fixes#392.
Validation: pnpm run check; Android aarch64 cargo check.
Android long-press saving now covers cross-origin images that the page
cannot read because of CORS. Like a browser's "Save image", the host
downloads remote http(s) sources into staging, while sources the page
owns are still read in their window. Both end in deliver_staged_file,
so the save panel, result and feedback are the same on every Android
version, with no storage permission.
- stage_file_from_url downloads through ContentService, which already
owns the URL rule for external imports.
- Compare origins with window.origin. srcdoc and about:blank frames
report location.origin as "null", which misrouted their same-origin
images and skipped their a[download] links.
- fetch_to_file returns the response Content-Type, so a remote image
without an extension in its URL is named from its type.
- Keep a path segment that is not valid percent-encoding as written,
so names like "100%.png" no longer make the save fail.
- File exports call staging and delivery commands through the host
safeInvoke, so failures show the backend message instead of a
generic one.
This replaces the original DownloadManager approach in #324.
Co-authored-by: Darkatse <39111369+Darkatse@users.noreply.github.com>
Centralize file picking, staging and delivery across platforms, with explicit cancellation outcomes and source ownership.
Remove superseded platform bridges and redundant tests, and document the shared contract and platform differences.
Validation: pnpm run check; Android aarch64 cargo check; 117 host tests.