Add device sync: mirror folders/files locally, background WorkManager engine
Build APK / build (push) Successful in 7m33s

- New native sync engine (SyncEngine/SyncWorker/ConflictResolveWorker,
  WorkManager-based) that mirrors selected Nextcloud folders or files to
  app-private local storage, periodically and on-demand ("Sync now"),
  with never-auto-resolved conflict notifications.
- Single files (not just folders) can now be marked "Sync to device".
- Live sync status (syncing/synced/conflicts) pushes from native to Dart
  over a new EventChannel; the persistent header chip/panel now reflects
  device-sync status instead of the WebDAV-refresh loading state, with a
  cloud_off/cloud_sync/cloud_done/cloud_alert icon set and an expandable
  conflicts list with in-app "Keep local"/"Use server" resolution.
  Per-item cloud_done/sync badges show on Files tiles.
- Share/Download short-circuit to the local copy for already-synced
  files instead of a fresh network fetch.
- Settings gained a Device Sync section (per-folder list, "Sync
  everything", Wi-Fi-only toggle, manual "Sync now").
- Fixed two release-only bugs found via on-device testing: Android's
  HttpURLConnection silently rejects the PROPFIND method (switched to
  OkHttp), and R8 was stripping WorkManager's reflection-instantiated
  internals (broadened proguard-rules.pro to keep androidx.work.**
  wholesale rather than chasing individual classes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 00:52:56 -04:00
co-authored by Claude Sonnet 5
parent 722b66f7b1
commit faaec5365f
21 changed files with 2023 additions and 42 deletions
+11 -3
View File
@@ -236,9 +236,17 @@ class _ShareSheetState extends State<ShareSheet> {
setState(() => _isSharingFile = true);
final messenger = ScaffoldMessenger.of(context);
try {
final tempDir = await getTemporaryDirectory();
final tempPath = p.join(tempDir.path, widget.item.name);
await provider.service!.downloadToFile(widget.item.path, tempPath);
// Already mirrored locally by device sync? Share that copy straight
// away instead of a fresh WebDAV fetch - see
// ServerProvider.localSyncedFilePath.
final tempPath =
await provider.localSyncedFilePath(widget.item) ??
await () async {
final tempDir = await getTemporaryDirectory();
final path = p.join(tempDir.path, widget.item.name);
await provider.service!.downloadToFile(widget.item.path, path);
return path;
}();
if (!mounted) return;
await SharePlus.instance.share(
ShareParams(