Skip to content

fix: save bucket exports from the WebView instead of dropping them - #229

Open
0xbrayo wants to merge 4 commits into
ActivityWatch:masterfrom
0xbrayo:fix/228-webview-export-download
Open

fix: save bucket exports from the WebView instead of dropping them#229
0xbrayo wants to merge 4 commits into
ActivityWatch:masterfrom
0xbrayo:fix/228-webview-export-download

Conversation

@0xbrayo

@0xbrayo 0xbrayo commented Aug 16, 2026

Copy link
Copy Markdown
Member

Fixes #228.

The bucket Export button fetched /api/0/buckets/.../export successfully (~39 ms in the report), then the bundled web UI saved the result with a blob: + <a download> click. Android WebView does not persist that, and the existing DownloadListener only fired ACTION_VIEW on the URL — so the bytes were discarded with no file, picker, toast, or crash.

Changes:

  • Inject a JS hook after the dashboard loads that intercepts those blob downloads
  • Shuttle the payload to native code in 256 KiB chunks (Binder’s ~1 MiB limit)
  • Open a Storage Access Framework “Save to…” picker and toast on success/failure
  • Also handle DownloadListener for blob: and loopback HTTP exports if the WebView does surface them

Verified with ./gradlew :mobile:testDebugUnitTest (including new hook / filename / chunk-reassembly tests). Not device-tested.

The web UI fetches /export successfully, then saves via a blob <a download>
which Android WebView does not persist. Intercept those downloads and write
them through the Storage Access Framework picker.

Fixes ActivityWatch#228
@greptile-apps

greptile-apps Bot commented Aug 16, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds native handling for dashboard blob and loopback export downloads, persists export payloads in cache, and serializes Storage Access Framework save requests across fragment recreation.

  • Injects a WebView hook that transfers blob exports through a chunked JavaScript bridge.
  • Queues exports and restores cached payload metadata across lifecycle recreation.
  • Adds save/share fallbacks, user feedback, filename and MIME handling, and focused unit tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt Adds chunked blob-export bridging, cached queue persistence, lifecycle restoration, document-picker saving, and download fallbacks; the previously reported queue and lifecycle issues are addressed.
mobile/src/test/java/net/activitywatch/android/fragments/WebUIFragmentTest.kt Adds unit coverage for hook generation, bridge reassembly, filename and MIME handling, queue ordering, cache persistence, and snapshot restoration.
mobile/src/main/res/values/strings.xml Adds localized messages for successful and failed export saves.

Sequence Diagram

sequenceDiagram
    participant UI as Dashboard Web UI
    participant Hook as Export JS Hook
    participant Bridge as WebAppInterface
    participant Queue as ExportSaveQueue
    participant Picker as Save-to Picker
    participant Storage as ContentResolver

    UI->>Hook: Click blob export link
    Hook->>Bridge: beginExport(name, MIME)
    loop 256 KiB chunks
        Hook->>Bridge: appendExport(chunk)
    end
    Hook->>Bridge: finishExport()
    Bridge->>Queue: Persist cache file and enqueue
    Queue->>Picker: Launch CreateDocument
    Picker-->>Queue: Return destination URI
    Queue->>Storage: Copy cached payload
    Storage-->>Queue: Save result
    Queue->>Queue: Delete cache and launch next
Loading

Reviews (4): Last reviewed commit: "address greptile review feedback (greplo..." | Re-trigger Greptile

Comment thread mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt Outdated
Queue overlapping exports so a later save cannot replace the picker payload,
and drop captured blobs on send or URL.revokeObjectURL.
@0xbrayo

0xbrayo commented Aug 16, 2026

Copy link
Copy Markdown
Member Author

🤖 Claude, on behalf of @0xbrayo

@greptile review

Persist queued exports to cache files and restore the Save-to queue after
fragment recreation so a picker result cannot drop the payload.
@0xbrayo

0xbrayo commented Aug 16, 2026

Copy link
Copy Markdown
Member Author

🤖 Claude, on behalf of @0xbrayo

@greptile review

Resume waiting exports in onStart after recreation during an async write,
when the restored queue has items but no in-flight picker.
@0xbrayo

0xbrayo commented Aug 16, 2026

Copy link
Copy Markdown
Member Author

🤖 Claude, on behalf of @0xbrayo

@greptile review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export button in bucket view silently fails: WebView fetches the export but never saves the file

1 participant