From fa619da867ece04798b44157fa82c3d4df86f114 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Nicolas <6371790+pynicolas@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:35:46 +0100 Subject: [PATCH] MainViewModel: avoid running IO operations on the main thread --- .../java/org/fairscan/app/MainViewModel.kt | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/fairscan/app/MainViewModel.kt b/app/src/main/java/org/fairscan/app/MainViewModel.kt index 96e09b8..520110a 100644 --- a/app/src/main/java/org/fairscan/app/MainViewModel.kt +++ b/app/src/main/java/org/fairscan/app/MainViewModel.kt @@ -20,6 +20,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -27,6 +28,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.fairscan.app.data.ImageRepository import org.fairscan.app.domain.CapturedPage import org.fairscan.app.domain.PageViewKey @@ -37,9 +39,13 @@ import org.fairscan.imageprocessing.encodeJpeg import org.opencv.android.Utils import org.opencv.core.Mat import org.opencv.imgproc.Imgproc +import java.util.concurrent.Executors class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode): ViewModel() { + // TODO ImageRepository should be made thread-safe + private val repositoryDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + private val _navigationState = MutableStateFlow(NavigationState.initial(launchMode)) val currentScreen: StateFlow = _navigationState.map { it.current } .stateIn(viewModelScope, SharingStarted.Eagerly, _navigationState.value.current) @@ -70,29 +76,40 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode fun rotateImage(id: String, clockwise: Boolean) { viewModelScope.launch { - imageRepository.rotate(id, clockwise) - _pages.value = imageRepository.pages() + val pages = withContext(repositoryDispatcher) { + imageRepository.rotate(id, clockwise) + imageRepository.pages() + } + _pages.value = pages } } fun movePage(id: String, newIndex: Int) { viewModelScope.launch { - imageRepository.movePage(id, newIndex) - _pages.value = imageRepository.pages() + val pages = withContext(repositoryDispatcher) { + imageRepository.movePage(id, newIndex) + imageRepository.pages() + } + _pages.value = pages } } fun deletePage(id: String) { viewModelScope.launch { - imageRepository.delete(id) - _pages.value = imageRepository.pages() + val pages = withContext(repositoryDispatcher) { + imageRepository.delete(id) + imageRepository.pages() + } + _pages.value = pages } } fun startNewDocument() { _pages.value = persistentListOf() viewModelScope.launch { - imageRepository.clear() + withContext(repositoryDispatcher) { + imageRepository.clear() + } } } @@ -108,12 +125,15 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode fun handleImageCaptured(capturedPage: CapturedPage) { viewModelScope.launch { - imageRepository.add( - capturedPage.pageJpeg, - compressJpeg(capturedPage.source, 90), - capturedPage.metadata, - ) - _pages.value = imageRepository.pages() + val pages = withContext(repositoryDispatcher) { + imageRepository.add( + capturedPage.pageJpeg, + compressJpeg(capturedPage.source, 90), + capturedPage.metadata, + ) + imageRepository.pages() + } + _pages.value = pages } }