From 66d28761a85583a9f22a7ff5308f7d0186456b88 Mon Sep 17 00:00:00 2001 From: Amir Khodak Date: Mon, 25 May 2026 14:06:19 +0000 Subject: [PATCH] v1.0.31: fix remaining sync loop triggers + icon redesign Three additional fixes found via live device logs: 1. Startup race window: FileObserver fires immediately after startWatching() before catchupScan coroutine runs, starting a 5s debounce with cooldown=0. Fixed by setting a 15s startup cooldown in watchPath() BEFORE calling watchDirRecursive. 2. Stale debounce bypass: debounce job started with cooldown=0 fires 5s later even after catchupScan has already set cooldown and started a catchup sync. Fixed by re-checking cooldown after the 5s delay and aborting if already active. 3. Debounce not cancelled by catchupScan: if a debounce was queued before catchupScan ran, catchupScan would enqueue a catchup sync AND the old debounce would fire 5s later enqueuing a second sync. Fixed by cancelling pending debounce in catchupScan before enqueue. Icon: four thick arcs (blue/red/green/orange) in a 4-way pinwheel with over/under ordering. White sync-arrow circle at center. Pure black background. Co-Authored-By: Claude Sonnet 4.6 --- .../com/syncflow/worker/FileWatchService.kt | 14 +++- .../res/drawable/ic_launcher_foreground.xml | 82 ++++++++++--------- version.properties | 4 +- 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/app/src/main/kotlin/com/syncflow/worker/FileWatchService.kt b/app/src/main/kotlin/com/syncflow/worker/FileWatchService.kt index ef73b15..5ee01fc 100644 --- a/app/src/main/kotlin/com/syncflow/worker/FileWatchService.kt +++ b/app/src/main/kotlin/com/syncflow/worker/FileWatchService.kt @@ -149,6 +149,9 @@ class FileWatchService : Service() { return } fileObservers[pairId] = mutableListOf() + // Set startup cooldown BEFORE registering watchers so inotify events that fire + // immediately on registration don't trigger the debounce before catchupScan runs. + syncCooldownUntil[pairId] = System.currentTimeMillis() + 15_000 watchDirRecursive(dir, pairId, wifiOnly, chargingOnly) Timber.d("FileWatchService: watching pair $pairId at $path (${fileObservers[pairId]?.size} dirs)") scope.launch { catchupScan(pairId, dir, wifiOnly, chargingOnly) } @@ -207,7 +210,10 @@ class FileWatchService : Service() { if (hasNew || hasModified || hasDeleted) { Timber.d("FileWatchService: catchup detected changes for pair $pairId, scheduling sync") val pair = syncPairDao.getById(pairId) ?: return - // Set cooldown so file writes during this sync don't immediately re-trigger + // Cancel any debounce that started before our startup cooldown was set + debounceJobs[pairId]?.cancel() + debounceJobs.remove(pairId) + // Hold cooldown for duration of sync + 60s settle syncCooldownUntil[pairId] = System.currentTimeMillis() + 120_000 val req = SyncWorker.buildOneTimeRequest(pairId, wifiOnly, chargingOnly) WorkManager.getInstance(applicationContext) @@ -238,6 +244,12 @@ class FileWatchService : Service() { debounceJobs[pairId]?.cancel() debounceJobs[pairId] = scope.launch { delay(5_000) + // Re-check: catchupScan or another path may have already set a cooldown + // and handled this sync while we were waiting. + if (System.currentTimeMillis() < (syncCooldownUntil[pairId] ?: 0L)) { + Timber.d("FileWatchService: debounce fired but cooldown active for pair $pairId, skipping") + return@launch + } val pair = syncPairDao.getById(pairId) if (pair == null || !pair.isEnabled) return@launch Timber.d("FileWatchService: triggering sync for pair $pairId after debounce") diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 8124678..88e597b 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -6,71 +6,79 @@ android:viewportHeight="108"> - + + android:pathData="M 54,36 A 18,18 0 1,1 45,70"/> - - - - + + android:pathData="M 54,72 A 18,18 0 1,1 63,38"/> - + + + + + android:pathData="M 36,54 A 18,18 0 1,1 69,63"/> - + + + + + android:pathData="M 45,54 A 9,9 0 1,0 63,54 A 9,9 0 1,0 45,54 Z"/> - + + android:pathData="M 46.5,54 A 7.5,7.5 0 1,0 61.5,54 A 7.5,7.5 0 1,0 46.5,54 Z"/> - + + android:pathData="M 54,46.5 L 57,50.5 L 51,50.5 Z"/> - + + android:pathData="M 54,61.5 L 51,57.5 L 57,57.5 Z"/> diff --git a/version.properties b/version.properties index 1acd08d..581ccc6 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -VERSION_NAME=1.0.30 -VERSION_CODE=31 +VERSION_NAME=1.0.31 +VERSION_CODE=32