Skip to content

fix(export): share the fallback export without loading it into memory - #351

Open
0xbrayo wants to merge 2 commits into
ActivityWatch:masterfrom
0xbrayo:fix/share-export-stream
Open

0xbrayo wants to merge 2 commits into
ActivityWatch:masterfrom
0xbrayo:fix/share-export-stream

Conversation

@0xbrayo

@0xbrayo 0xbrayo commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Part of #333.

#304 made large exports stream to a cache file so they are never held in memory as a String. The share-sheet fallback (used when CreateDocument can't be launched) still did file.writeText(pending.readContent()), which reads the entire export into a String and writes it back out, on the UI thread. A large bucket export on that path could OOM or ANR.

Change

  • The fallback now moves the cached export into cacheDir/shared-exports/<uuid>/<filename> (moveExportForSharing) and shares it from there through FileProvider. A new cache-path entry covers that directory.
    • A rename within cacheDir is instant, so the share stays synchronous on the UI thread. There's no background work to race a second export or to be lost if the activity is recreated.
    • Each share gets its own directory, so exports with the same filename never overwrite each other.
    • Shared copies older than a day are removed on the next share.
  • PendingExport.readContent() had no remaining production caller, so it is removed. The one test that used it now reads cacheFile directly.

Testing

  • New WebUIFragmentTest cases: two exports with the same filename are shared as separate files with their own contents, and old shared exports are pruned.
  • ./gradlew :mobile:testStandardDebugUnitTest passes.

When the system file picker is unavailable, shareExport read the whole
cached export into a String and wrote it back out on the UI thread,
undoing the streaming the main save path gained in ActivityWatch#304. Copy the cache
file as a stream on a background thread, then open the share sheet. The
now-unused PendingExport.readContent() is removed.
@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable new issue remains.

Summary

The fallback now moves each cached export into its own sharing directory instead of loading the export into memory.

  • Adds a matching FileProvider cache path.
  • Removes shared exports older than a day on the next share.
  • Adds tests for same-name exports and cleanup.
  • Both earlier findings are addressed: exports no longer share a destination file, and no background copy can outlive the fragment.

Reviews (2) · Last reviewed commit: "fix(export): share the fallback export b..." · Reviewed by Greptile

Comment thread mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt Outdated
Comment thread mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt Outdated
@0xbrayo
0xbrayo force-pushed the fix/share-export-stream branch from f0b1c15 to 72818b9 Compare October 8, 2026 19:37
The background copy raced a second export (same filename, overwrite)
and was lost if the activity was recreated mid-copy. Move the cached
export into its own directory under cacheDir/shared-exports instead: a
rename within cacheDir is instant, so the share stays synchronous on
the UI thread with no background work, and never reads the export into
memory. Shared copies older than a day are removed on the next share.
@0xbrayo 0xbrayo changed the title fix(export): stream the share-sheet fallback copy off the UI thread fix(export): share the fallback export without loading it into memory Oct 8, 2026
@0xbrayo

0xbrayo commented Oct 8, 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.

1 participant