Split MainViewModel: extract HomeViewModel
This commit is contained in:
committed by
pynicolas
parent
7c53dcface
commit
d4a3c78c23
@@ -53,7 +53,7 @@ import org.fairscan.app.ui.Screen
|
|||||||
import org.fairscan.app.ui.components.rememberCameraPermissionState
|
import org.fairscan.app.ui.components.rememberCameraPermissionState
|
||||||
import org.fairscan.app.ui.screens.AboutScreen
|
import org.fairscan.app.ui.screens.AboutScreen
|
||||||
import org.fairscan.app.ui.screens.DocumentScreen
|
import org.fairscan.app.ui.screens.DocumentScreen
|
||||||
import org.fairscan.app.ui.screens.HomeScreen
|
import org.fairscan.app.ui.screens.home.HomeScreen
|
||||||
import org.fairscan.app.ui.screens.LibrariesScreen
|
import org.fairscan.app.ui.screens.LibrariesScreen
|
||||||
import org.fairscan.app.ui.screens.camera.CameraEvent
|
import org.fairscan.app.ui.screens.camera.CameraEvent
|
||||||
import org.fairscan.app.ui.screens.camera.CameraScreen
|
import org.fairscan.app.ui.screens.camera.CameraScreen
|
||||||
@@ -61,6 +61,7 @@ import org.fairscan.app.ui.screens.camera.CameraViewModel
|
|||||||
import org.fairscan.app.ui.screens.export.ExportScreenWrapper
|
import org.fairscan.app.ui.screens.export.ExportScreenWrapper
|
||||||
import org.fairscan.app.ui.screens.export.ExportViewModel
|
import org.fairscan.app.ui.screens.export.ExportViewModel
|
||||||
import org.fairscan.app.ui.screens.export.PdfGenerationActions
|
import org.fairscan.app.ui.screens.export.PdfGenerationActions
|
||||||
|
import org.fairscan.app.ui.screens.home.HomeViewModel
|
||||||
import org.fairscan.app.ui.theme.FairScanTheme
|
import org.fairscan.app.ui.theme.FairScanTheme
|
||||||
import org.opencv.android.OpenCVLoader
|
import org.opencv.android.OpenCVLoader
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
initLibraries()
|
initLibraries()
|
||||||
val viewModel: MainViewModel by viewModels { MainViewModel.getFactory(this) }
|
val viewModel: MainViewModel by viewModels { MainViewModel.getFactory(this) }
|
||||||
|
val homeViewModel: HomeViewModel by viewModels { HomeViewModel.getFactory(this) }
|
||||||
val cameraViewModel: CameraViewModel by viewModels { CameraViewModel.getFactory(this) }
|
val cameraViewModel: CameraViewModel by viewModels { CameraViewModel.getFactory(this) }
|
||||||
val exportViewModel: ExportViewModel by viewModels { ExportViewModel.getFactory(this) }
|
val exportViewModel: ExportViewModel by viewModels { ExportViewModel.getFactory(this) }
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
@@ -91,7 +93,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val liveAnalysisState by cameraViewModel.liveAnalysisState.collectAsStateWithLifecycle()
|
val liveAnalysisState by cameraViewModel.liveAnalysisState.collectAsStateWithLifecycle()
|
||||||
val document by viewModel.documentUiModel.collectAsStateWithLifecycle()
|
val document by viewModel.documentUiModel.collectAsStateWithLifecycle()
|
||||||
val cameraPermission = rememberCameraPermissionState()
|
val cameraPermission = rememberCameraPermissionState()
|
||||||
val savePdf = { savePdf(exportViewModel.getFinalPdf(), viewModel, exportViewModel) }
|
val savePdf = { savePdf(exportViewModel.getFinalPdf(), homeViewModel, exportViewModel) }
|
||||||
val storagePermissionLauncher = rememberLauncherForActivityResult(
|
val storagePermissionLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.RequestPermission()
|
ActivityResultContracts.RequestPermission()
|
||||||
) { isGranted ->
|
) { isGranted ->
|
||||||
@@ -114,7 +116,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
)
|
)
|
||||||
when (val screen = currentScreen) {
|
when (val screen = currentScreen) {
|
||||||
is Screen.Main.Home -> {
|
is Screen.Main.Home -> {
|
||||||
val recentDocs by viewModel.recentDocuments.collectAsStateWithLifecycle()
|
val recentDocs by homeViewModel.recentDocuments.collectAsStateWithLifecycle()
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
cameraPermission = cameraPermission,
|
cameraPermission = cameraPermission,
|
||||||
currentDocument = document,
|
currentDocument = document,
|
||||||
@@ -210,7 +212,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
private fun savePdf(
|
private fun savePdf(
|
||||||
generatedPdf: GeneratedPdf?,
|
generatedPdf: GeneratedPdf?,
|
||||||
viewModel: MainViewModel,
|
homeViewModel: HomeViewModel,
|
||||||
exportViewModel: ExportViewModel
|
exportViewModel: ExportViewModel
|
||||||
) {
|
) {
|
||||||
if (generatedPdf == null)
|
if (generatedPdf == null)
|
||||||
@@ -220,7 +222,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
appScope.launch {
|
appScope.launch {
|
||||||
try {
|
try {
|
||||||
val targetFile = exportViewModel.saveFile(generatedPdf.file)
|
val targetFile = exportViewModel.saveFile(generatedPdf.file)
|
||||||
viewModel.addRecentDocument(targetFile.absolutePath, generatedPdf.pageCount)
|
homeViewModel.addRecentDocument(targetFile.absolutePath, generatedPdf.pageCount)
|
||||||
|
|
||||||
suspendCancellableCoroutine { continuation ->
|
suspendCancellableCoroutine { continuation ->
|
||||||
MediaScannerConnection.scanFile(
|
MediaScannerConnection.scanFile(
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package org.fairscan.app
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import androidx.datastore.core.DataStore
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
@@ -31,16 +30,12 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.fairscan.app.data.ImageRepository
|
import org.fairscan.app.data.ImageRepository
|
||||||
import org.fairscan.app.data.recentDocumentsDataStore
|
|
||||||
import org.fairscan.app.ui.NavigationState
|
import org.fairscan.app.ui.NavigationState
|
||||||
import org.fairscan.app.ui.Screen
|
import org.fairscan.app.ui.Screen
|
||||||
import org.fairscan.app.ui.state.DocumentUiModel
|
import org.fairscan.app.ui.state.DocumentUiModel
|
||||||
import org.fairscan.app.ui.state.RecentDocumentUiState
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class MainViewModel(
|
class MainViewModel(
|
||||||
private val imageRepository: ImageRepository,
|
private val imageRepository: ImageRepository
|
||||||
private val recentDocumentsDataStore: DataStore<RecentDocuments>,
|
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -48,10 +43,7 @@ class MainViewModel(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
||||||
val app = context.applicationContext as FairScanApp
|
val app = context.applicationContext as FairScanApp
|
||||||
return MainViewModel(
|
return MainViewModel(app.appContainer.imageRepository) as T
|
||||||
app.appContainer.imageRepository,
|
|
||||||
context.recentDocumentsDataStore,
|
|
||||||
) as T
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,41 +108,6 @@ class MainViewModel(
|
|||||||
return bytes?.let { BitmapFactory.decodeByteArray(it, 0, it.size) }
|
return bytes?.let { BitmapFactory.decodeByteArray(it, 0, it.size) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val recentDocuments: StateFlow<List<RecentDocumentUiState>> =
|
|
||||||
recentDocumentsDataStore.data.map {
|
|
||||||
it.documentsList.map {
|
|
||||||
doc ->
|
|
||||||
RecentDocumentUiState(
|
|
||||||
file = File(doc.filePath),
|
|
||||||
saveTimestamp = doc.createdAt,
|
|
||||||
pageCount = doc.pageCount,
|
|
||||||
)
|
|
||||||
}.filter { doc -> doc.file.exists() }
|
|
||||||
}.stateIn(
|
|
||||||
scope = viewModelScope,
|
|
||||||
started = SharingStarted.WhileSubscribed(5_000),
|
|
||||||
initialValue = emptyList(),
|
|
||||||
)
|
|
||||||
fun addRecentDocument(filePath: String, pageCount: Int) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
recentDocumentsDataStore.updateData { current ->
|
|
||||||
val newDoc = RecentDocument.newBuilder()
|
|
||||||
.setFilePath(filePath)
|
|
||||||
.setPageCount(pageCount)
|
|
||||||
.setCreatedAt(System.currentTimeMillis())
|
|
||||||
.build()
|
|
||||||
current.toBuilder()
|
|
||||||
.addDocuments(0, newDoc)
|
|
||||||
.also { builder ->
|
|
||||||
while (builder.documentsCount > 3) {
|
|
||||||
builder.removeDocuments(builder.documentsCount - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleImageCaptured(jpegBytes: ByteArray) {
|
fun handleImageCaptured(jpegBytes: ByteArray) {
|
||||||
imageRepository.add(jpegBytes)
|
imageRepository.add(jpegBytes)
|
||||||
_pageIds.value = imageRepository.imageIds()
|
_pageIds.value = imageRepository.imageIds()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.fairscan.app.ui.screens
|
package org.fairscan.app.ui.screens.home
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Pierre-Yves Nicolas
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.fairscan.app.ui.screens.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.fairscan.app.RecentDocument
|
||||||
|
import org.fairscan.app.RecentDocuments
|
||||||
|
import org.fairscan.app.data.recentDocumentsDataStore
|
||||||
|
import org.fairscan.app.ui.state.RecentDocumentUiState
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class HomeViewModel(private val recentDocumentsDataStore: DataStore<RecentDocuments>): ViewModel() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getFactory(context: Context) = object : ViewModelProvider.Factory {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
||||||
|
return HomeViewModel(context.recentDocumentsDataStore) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val recentDocuments: StateFlow<List<RecentDocumentUiState>> =
|
||||||
|
recentDocumentsDataStore.data.map {
|
||||||
|
it.documentsList.map {
|
||||||
|
doc ->
|
||||||
|
RecentDocumentUiState(
|
||||||
|
file = File(doc.filePath),
|
||||||
|
saveTimestamp = doc.createdAt,
|
||||||
|
pageCount = doc.pageCount,
|
||||||
|
)
|
||||||
|
}.filter { doc -> doc.file.exists() }
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(5_000),
|
||||||
|
initialValue = emptyList(),
|
||||||
|
)
|
||||||
|
fun addRecentDocument(filePath: String, pageCount: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
recentDocumentsDataStore.updateData { current ->
|
||||||
|
val newDoc = RecentDocument.newBuilder()
|
||||||
|
.setFilePath(filePath)
|
||||||
|
.setPageCount(pageCount)
|
||||||
|
.setCreatedAt(System.currentTimeMillis())
|
||||||
|
.build()
|
||||||
|
current.toBuilder()
|
||||||
|
.addDocuments(0, newDoc)
|
||||||
|
.also { builder ->
|
||||||
|
while (builder.documentsCount > 3) {
|
||||||
|
builder.removeDocuments(builder.documentsCount - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user