v1.0.63: live sync progress counters, pause/resume, .gitignore fix
Build & Release APK / build (push) Has been cancelled
Build & Release APK / build (push) Has been cancelled
- SyncEngine: accepts onProgress callback — emits uploaded/downloaded/ deleted/bytes counts atomically as each file completes - SyncWorker: streams progress to WorkManager data so the UI can poll it live; reports per-run counters in the completion notification; adds pause/resume support - HomeViewModel/PairDetailViewModel: subscribe to live WorkManager progress and surface it via SyncProgress state - SyncPairEntity/SyncPairDao/SyncDatabase: persist last-run counters (uploaded, downloaded, deleted, bytesTransferred) in the DB with a Room migration (v3→v4) - AppModule: provides WorkManager as an injectable singleton - .gitignore: add .kotlin/ to exclude compiler session files Security: no new issues — all logging via Timber (debug-only), DB queries use Room parameterized API, file sharing via FileProvider. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,16 +4,20 @@ import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkQuery
|
||||
import com.syncflow.data.db.SyncPairDao
|
||||
import com.syncflow.data.db.entities.SyncPairEntity
|
||||
import com.syncflow.domain.model.ScheduleType
|
||||
import com.syncflow.domain.model.SyncStatus
|
||||
import com.syncflow.ui.shared.SyncProgress
|
||||
import com.syncflow.worker.FileWatchService
|
||||
import com.syncflow.worker.SyncWorker
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
@@ -28,6 +32,23 @@ class HomeViewModel @Inject constructor(
|
||||
val syncPairs = syncPairDao.observeAll()
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||
|
||||
val syncProgressMap: kotlinx.coroutines.flow.StateFlow<Map<Long, SyncProgress>> =
|
||||
workManager.getWorkInfosFlow(WorkQuery.fromStates(WorkInfo.State.RUNNING))
|
||||
.map { infos ->
|
||||
infos
|
||||
.mapNotNull { info ->
|
||||
val tag = info.tags.firstOrNull { it.startsWith("sync_") } ?: return@mapNotNull null
|
||||
val pairId = tag.removePrefix("sync_").toLongOrNull() ?: return@mapNotNull null
|
||||
val up = info.progress.getInt(SyncWorker.KEY_PROGRESS_UPLOADED, 0)
|
||||
val down = info.progress.getInt(SyncWorker.KEY_PROGRESS_DOWNLOADED, 0)
|
||||
val del = info.progress.getInt(SyncWorker.KEY_PROGRESS_DELETED, 0)
|
||||
val bytes = info.progress.getLong(SyncWorker.KEY_PROGRESS_BYTES, 0L)
|
||||
if (up > 0 || down > 0 || del > 0) pairId to SyncProgress(up, down, del, bytes) else null
|
||||
}
|
||||
.toMap()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())
|
||||
|
||||
fun triggerSync(pair: SyncPairEntity) {
|
||||
val req = SyncWorker.buildOneTimeRequest(pair.id, wifiOnly = false, chargingOnly = false)
|
||||
workManager.enqueue(req)
|
||||
|
||||
Reference in New Issue
Block a user