package com.syncflow.ui.home 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 @HiltViewModel class HomeViewModel @Inject constructor( private val syncPairDao: SyncPairDao, private val workManager: WorkManager, @ApplicationContext private val context: Context, ) : ViewModel() { val syncPairs = syncPairDao.observeAll() .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) val syncProgressMap: kotlinx.coroutines.flow.StateFlow> = 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) } fun pauseSync(pair: SyncPairEntity) { workManager.cancelAllWorkByTag("sync_${pair.id}") viewModelScope.launch { syncPairDao.updateStatus(pair.id, SyncStatus.PAUSED) } } fun toggleEnabled(pair: SyncPairEntity) { viewModelScope.launch { val nowEnabled = !pair.isEnabled syncPairDao.update(pair.copy(isEnabled = nowEnabled)) if (nowEnabled) { when (pair.scheduleType) { ScheduleType.ON_CHANGE -> FileWatchService.start(context) ScheduleType.MANUAL -> { /* nothing */ } else -> { val req = SyncWorker.buildPeriodicRequest( pair.id, pair.scheduleIntervalMinutes.toLong().coerceAtLeast(15), pair.wifiOnly, pair.chargingOnly, ) workManager.enqueueUniquePeriodicWork("periodic_${pair.id}", ExistingPeriodicWorkPolicy.UPDATE, req) } } } else { workManager.cancelAllWorkByTag("sync_${pair.id}") // Refresh watcher (it will stop itself if no ON_CHANGE pairs remain) if (pair.scheduleType == ScheduleType.ON_CHANGE) { FileWatchService.start(context) } } } } }