mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 11:48:24 +00:00
Tapping `<input type="file">` anywhere in Amethyst was a silent no-op: no picker, no error, nothing logged. An Android WebView shows no chooser of its own — the app must override `WebChromeClient.onShowFileChooser`, and none of the four WebView hosts did. The base implementation returns false, and for a target of API 21+ there is no legacy fallback, so every file upload in the in-app browser, in nSites and in napplets was impossible. All four hosts now open the picker: - NappletBrowserActivity (full-screen browser) and NappletHostActivity (full-screen napplet/nSite sandbox) own an Activity, so they run the picker directly through an ActivityResultLauncher. - NappletBrowserService and NappletHostService render an embedded surface from a windowless Service in the keyless `:napplet` process and have no Activity to launch from. They send the request's *description* — accept list, multi-select, title — to the main process over the existing Messenger contract; WebFileChooserCoordinator builds the Intent there and collects the result in the throwaway WebFileChooserActivity. Shipping data instead of a ready-made Intent keeps the sandbox able to ask the trusted process for a file picker and for nothing else. URI read grants are per-UID, so the picked `content://` URIs are readable by the WebView in `:napplet` with no re-granting, and allowContentAccess stays off. Two details that decide whether this actually works in practice: - The page's `filePathCallback` must fire on every path. WebView keeps a file input busy until it does, so a dropped callback (user cancelled, session torn down, no app to handle the Intent) leaves that input permanently dead for the life of the page. PendingFileChooser guarantees exactly-once delivery and carries a request id so a result that outlived its request is dropped rather than fed to whichever input is waiting now. - Android's own FileChooserParams.createIntent() keeps only the first `accept` entry and drops multi-select, so `accept="image/png,image/jpeg" multiple` would offer PNGs only, one at a time. FileChooserAccept resolves the whole list — extensions included — into a type plus EXTRA_MIME_TYPES, widening to a family wildcard rather than narrowing below what the page asked for. It is pure and unit-tested in commonMain. NappletHostService had no chrome client at all, so it gains one. Its WebView is built from a Service context with no window token to attach a dialog to, so the new client also dismisses JS alert/confirm/prompt instead of opting into the default dialog handling. Camera capture (`accept` with `capture`) and getUserMedia still fall back to the picker; `onPermissionRequest` remains unimplemented and is left for a separate change. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FxfdHeR9Ry4qALXHT5Sf1Q