Avoid resolution of current page in DocumentScreen

This commit is contained in:
Pierre-Yves Nicolas
2026-04-01 10:17:57 +02:00
parent b258082ce1
commit 215f57bb74
5 changed files with 49 additions and 45 deletions

View File

@@ -62,7 +62,6 @@ import org.fairscan.app.ui.screens.about.createEmailWithImageIntent
import org.fairscan.app.ui.screens.camera.CameraEvent
import org.fairscan.app.ui.screens.camera.CameraScreen
import org.fairscan.app.ui.screens.camera.CameraViewModel
import org.fairscan.app.ui.screens.document.DocumentUiState
import org.fairscan.app.ui.screens.export.ExportActions
import org.fairscan.app.ui.screens.export.ExportEvent
import org.fairscan.app.ui.screens.export.ExportResult
@@ -182,9 +181,9 @@ class MainActivity : ComponentActivity() {
uiState = documentUiState,
navigation = navigation,
onExportClick = onExportClick,
onDeleteImage = { id -> viewModel.deletePage(id) },
onRotateImage = { id, clockwise -> viewModel.rotateImage(id, clockwise) },
onToggleColorMode = { id -> viewModel.togglePageColorMode(id) },
onDeleteImage = { viewModel.deleteCurrentPage() },
onRotateImage = { clockwise -> viewModel.rotateCurrentPage(clockwise) },
onToggleColorMode = { viewModel.toggleCurrentPageColorMode() },
onPageReorder = { id, newIndex -> viewModel.movePage(id, newIndex) },
onPageSelected = viewModel::onPageSelected
)

View File

@@ -14,7 +14,6 @@
*/
package org.fairscan.app
import android.graphics.Bitmap
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.collections.immutable.persistentListOf
@@ -43,6 +42,7 @@ import org.fairscan.app.ui.screens.document.DocumentUiState
import org.fairscan.app.ui.state.DocumentUiModel
import org.fairscan.app.ui.state.PageThumbnail
import org.fairscan.imageprocessing.ColorMode
import java.lang.IllegalStateException
import kotlin.math.min
@OptIn(ExperimentalCoroutinesApi::class)
@@ -77,15 +77,16 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode
private val _currentPageIndex = MutableStateFlow(0)
val currentPageUiState: Flow<CurrentPageUiState> =
private val currentPageUiState: Flow<CurrentPageUiState?> =
combine(_currentPageIndex, _pages) { index, pages -> pages.getOrNull(index) }
.mapLatest { page ->
page?.let {
CurrentPageUiState(
page.id,
imageRepository.jpegBytes(it.key())?.toBitmap(),
page.colorMode
)
} ?: CurrentPageUiState()
}
}
.flowOn(Dispatchers.IO)
@@ -95,7 +96,7 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode
}
.stateIn(
viewModelScope, SharingStarted.Eagerly,
DocumentUiState(0, CurrentPageUiState(), DocumentUiModel())
DocumentUiState(0, null, DocumentUiModel())
)
fun onPageSelected(index: Int) {
@@ -116,10 +117,10 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode
_navigationState.update { stack -> stack.navigateBack() }
}
fun rotateImage(id: String, clockwise: Boolean) {
fun rotateCurrentPage(clockwise: Boolean) {
viewModelScope.launch {
val pages = withContext(Dispatchers.IO) {
imageRepository.rotate(id, clockwise)
imageRepository.rotate(currentPage().id, clockwise)
imageRepository.pages()
}
_pages.value = pages
@@ -136,10 +137,10 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode
}
}
fun deletePage(id: String) {
fun deleteCurrentPage() {
viewModelScope.launch {
val pages = withContext(Dispatchers.IO) {
imageRepository.delete(id)
imageRepository.delete(currentPage().id)
imageRepository.pages()
}
@@ -153,14 +154,14 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode
}
}
fun togglePageColorMode(id: String) {
fun toggleCurrentPageColorMode() {
viewModelScope.launch {
val currentColorMode = _pages.value.find { p -> p.id == id }?.colorMode
currentColorMode?.let {
val currentPage = currentPage()
currentPage.colorMode?.let {
val newColorMode =
if (it == ColorMode.COLOR) ColorMode.GRAYSCALE else ColorMode.COLOR
val pages = withContext(Dispatchers.IO) {
imageRepository.setColorMode(id, newColorMode)
imageRepository.setColorMode(currentPage.id, newColorMode)
imageRepository.pages()
}
_pages.value = pages
@@ -168,6 +169,13 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode
}
}
private fun currentPage(): ScanPage {
val index = _currentPageIndex.value
val pages = _pages.value
return pages.getOrNull(index) ?: throw IllegalStateException(
"No current page for index $index (${pages.size} pages)")
}
fun startNewDocument() {
_pages.value = persistentListOf()
viewModelScope.launch {

View File

@@ -86,9 +86,9 @@ fun DocumentScreen(
uiState: DocumentUiState,
navigation: Navigation,
onExportClick: () -> Unit,
onDeleteImage: (String) -> Unit,
onRotateImage: (String, Boolean) -> Unit,
onToggleColorMode: (String) -> Unit,
onDeleteImage: () -> Unit,
onRotateImage: (Boolean) -> Unit,
onToggleColorMode: () -> Unit,
onPageReorder: (String, Int) -> Unit,
onPageSelected: (Int) -> Unit,
) {
@@ -130,7 +130,7 @@ fun DocumentScreen(
title = stringResource(R.string.delete_page),
message = stringResource(R.string.delete_page_warning),
showDialog = showDeletePageDialog
) { onDeleteImage(document.pageId(currentPageIndex)) }
) { onDeleteImage() }
}
}
}
@@ -138,14 +138,13 @@ fun DocumentScreen(
@Composable
private fun DocumentPreview(
uiState: DocumentUiState,
onDeleteImage: (String) -> Unit,
onRotateImage: (String, Boolean) -> Unit,
onToggleColorMode: (String) -> Unit,
onDeleteImage: () -> Unit,
onRotateImage: (Boolean) -> Unit,
onToggleColorMode: () -> Unit,
modifier: Modifier,
) {
val currentPageIndex = uiState.currentPageIndex
val document = uiState.document
val imageId = document.pageId(currentPageIndex)
Column (
modifier = modifier
.background(MaterialTheme.colorScheme.surfaceContainerLow)
@@ -153,10 +152,11 @@ private fun DocumentPreview(
Box (
modifier = Modifier.fillMaxSize()
) {
val bitmap = uiState.currentPage.bitmap
if (bitmap != null) {
val bitmap = uiState.currentPage?.bitmap
val pageId = uiState.currentPage?.id
if (bitmap != null && pageId != null) {
val imageBitmap = bitmap.asImageBitmap()
val zoomState = remember(imageId) {
val zoomState = remember(pageId) {
ZoomState(
contentSize = Size(bitmap.width.toFloat(), bitmap.height.toFloat())
)
@@ -175,20 +175,20 @@ private fun DocumentPreview(
)
}
}
uiState.currentPage.colorMode?.let {
uiState.currentPage?.colorMode?.let {
ColorModeButton(
currentColorMode = it,
onToggle = { onToggleColorMode(imageId) },
onToggle = { onToggleColorMode() },
modifier = Modifier
.align(Alignment.BottomStart)
.padding(8.dp)
)
}
RotationButtons(imageId, onRotateImage, Modifier.align(Alignment.BottomCenter))
RotationButtons(onRotateImage, Modifier.align(Alignment.BottomCenter))
SecondaryActionButton(
Icons.Outlined.Delete,
contentDescription = stringResource(R.string.delete_page),
onClick = { onDeleteImage(imageId) },
onClick = { onDeleteImage() },
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(8.dp)
@@ -210,8 +210,7 @@ private fun DocumentPreview(
@Composable
fun RotationButtons(
imageId: String,
onRotateImage: (String, Boolean) -> Unit,
onRotateImage: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
// RotateLeft on the left, RotateRight on the right: for both LTR and RTL languages
@@ -222,14 +221,14 @@ fun RotationButtons(
SecondaryActionButton(
icon = Icons.Default.RotateLeft,
contentDescription = stringResource(R.string.rotate_left),
onClick = { onRotateImage(imageId, false) }
onClick = { onRotateImage(false) }
)
Spacer(Modifier.width(8.dp))
@Suppress("DEPRECATION")
SecondaryActionButton(
icon = Icons.Default.RotateRight,
contentDescription = stringResource(R.string.rotate_right),
onClick = { onRotateImage(imageId, true) }
onClick = { onRotateImage(true) }
)
}
}
@@ -307,12 +306,12 @@ fun DocumentScreenPreview() {
LocalContext.current
)
DocumentScreen(
uiState = DocumentUiState(1, CurrentPageUiState(image, COLOR), document),
uiState = DocumentUiState(1, CurrentPageUiState("123",image, COLOR), document),
navigation = dummyNavigation(),
onExportClick = {},
onDeleteImage = { _ -> },
onRotateImage = { _,_ -> },
onToggleColorMode = { _ -> },
onDeleteImage = { },
onRotateImage = { _ -> },
onToggleColorMode = { },
onPageReorder = { _,_ -> },
onPageSelected = { _ -> },
)

View File

@@ -20,11 +20,12 @@ import org.fairscan.imageprocessing.ColorMode
data class DocumentUiState(
val currentPageIndex: Int,
val currentPage: CurrentPageUiState,
val currentPage: CurrentPageUiState?,
val document: DocumentUiModel,
)
data class CurrentPageUiState(
val bitmap: Bitmap? = null,
val colorMode: ColorMode? = null,
val id: String,
val bitmap: Bitmap?,
val colorMode: ColorMode?,
)

View File

@@ -25,9 +25,6 @@ data class DocumentUiModel(
fun pageCount(): Int {
return pages.size
}
fun pageId(index: Int): String {
return pages[index].key.pageId
}
fun isEmpty(): Boolean {
return pages.isEmpty()
}