Add device sync: mirror folders/files locally, background WorkManager engine
Build APK / build (push) Successful in 7m33s
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:
@@ -0,0 +1,38 @@
|
||||
/// A pending device-sync conflict (a file changed both locally and on the
|
||||
/// server since the last sync) - mirrors `SyncStatusBus.Conflict` on the
|
||||
/// native side exactly, see `SyncEngine.kt`/`server.md`'s "Device sync"
|
||||
/// section.
|
||||
class SyncConflictInfo {
|
||||
final String accountId;
|
||||
final String fileId;
|
||||
final String remotePath;
|
||||
final String relPath;
|
||||
final String name;
|
||||
|
||||
const SyncConflictInfo({
|
||||
required this.accountId,
|
||||
required this.fileId,
|
||||
required this.remotePath,
|
||||
required this.relPath,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
factory SyncConflictInfo.fromMap(Map<dynamic, dynamic> map) {
|
||||
return SyncConflictInfo(
|
||||
accountId: map['accountId'] as String? ?? '',
|
||||
fileId: map['fileId'] as String? ?? '',
|
||||
remotePath: map['remotePath'] as String? ?? '',
|
||||
relPath: map['relPath'] as String? ?? '',
|
||||
name: map['name'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A single item's device-sync status, used to badge its tile in the
|
||||
/// Files view.
|
||||
enum SyncItemStatus { none, syncing, synced, conflict }
|
||||
|
||||
/// The persistent header chip/panel's overall status (account-wide, not
|
||||
/// scoped to whatever folder is currently browsed) - see
|
||||
/// `ServerProvider.syncHeaderStatus`.
|
||||
enum SyncHeaderStatus { off, syncing, done, alert }
|
||||
Reference in New Issue
Block a user