Ability to delete one of the scanned pages

This commit is contained in:
Pierre-Yves Nicolas
2025-06-04 16:41:38 +02:00
parent a6e1d8cc51
commit ebd5453b65
4 changed files with 68 additions and 23 deletions

View File

@@ -33,4 +33,9 @@ class ImageRepository(appFilesDir: File) {
throw IllegalArgumentException("No image for id: $id") throw IllegalArgumentException("No image for id: $id")
} }
fun delete(id: String) {
val file = File(scanDir, id)
file.delete()
fileNames.remove(id)
}
} }

View File

@@ -116,6 +116,11 @@ class MainViewModel(
_pageIds.value = imageRepository.imageIds() _pageIds.value = imageRepository.imageIds()
} }
fun deletePage(id: String) {
imageRepository.delete(id)
_pageIds.value = imageRepository.imageIds()
}
fun pageCount(): Int = pageIds.value.size fun pageCount(): Int = pageIds.value.size
fun getBitmap(id: String): Bitmap { fun getBitmap(id: String): Bitmap {

View File

@@ -1,8 +1,10 @@
package org.mydomain.myscan.view package org.mydomain.myscan.view
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@@ -14,10 +16,12 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.BottomAppBar import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.Button import androidx.compose.material3.Button
@@ -35,7 +39,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@@ -49,7 +52,6 @@ fun FinalizeDocumentScreen(
onSavePressed: () -> Unit, onSavePressed: () -> Unit,
onSharePressed: () -> Unit, onSharePressed: () -> Unit,
) { ) {
val pageIds by viewModel.pageIds.collectAsStateWithLifecycle()
Scaffold ( Scaffold (
topBar = { topBar = {
TopAppBar( TopAppBar(
@@ -80,12 +82,21 @@ fun FinalizeDocumentScreen(
} }
} }
} }
) { padding -> ) { padding -> DocumentPreview(padding, viewModel) }
Column(modifier = Modifier }
@Composable
private fun DocumentPreview(
padding: PaddingValues,
viewModel: MainViewModel
) {
val pageIds by viewModel.pageIds.collectAsStateWithLifecycle()
Column(
modifier = Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(padding)) { .padding(padding)
) {
Text( Text(
"Pages", "Pages",
modifier = Modifier.padding(start = 16.dp, top = 16.dp), modifier = Modifier.padding(start = 16.dp, top = 16.dp),
@@ -100,16 +111,28 @@ fun FinalizeDocumentScreen(
) { ) {
pageIds.forEachIndexed { index, id -> pageIds.forEachIndexed { index, id ->
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
// TODO Display small images rather than big ones
// TODO Make it possible to zoom on an image
Box {
Image( Image(
bitmap = viewModel.getBitmap(id).asImageBitmap(), bitmap = viewModel.getBitmap(id).asImageBitmap(),
contentDescription = "Page ${index + 1}", contentDescription = "Page ${index + 1}",
modifier = Modifier modifier = Modifier
.size(160.dp) .size(160.dp)
.padding(4.dp)
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.border(1.dp, Color.DarkGray, RoundedCornerShape(8.dp)), .border(1.dp, Color.DarkGray)
contentScale = ContentScale.Fit
) )
Text("Page ${index + 1}")
IconButton(
onClick = { viewModel.deletePage(id) },
modifier = Modifier
.align(Alignment.TopEnd)
.size(24.dp)
.background(Color.Black.copy(alpha = 0.5f), shape = CircleShape)
) {
Icon(Icons.Default.Delete, contentDescription = "Delete", tint = Color.White)
}
} }
} }
} }

View File

@@ -35,6 +35,18 @@ class ImageRepositoryTest {
assertThat(repo.getContent(repo.imageIds()[0])).isEqualTo(bytes) assertThat(repo.getContent(repo.imageIds()[0])).isEqualTo(bytes)
} }
@Test
fun delete_image() {
val repo = repo()
val bytes = byteArrayOf(101, 102, 103)
repo.add(bytes)
assertThat(repo.imageIds()).hasSize(1)
repo.delete(repo.imageIds()[0])
assertThat(repo.imageIds()).isEmpty()
val repo2 = repo()
assertThat(repo2.imageIds()).isEmpty()
}
@Test @Test
fun `should find existing files at initialization`() { fun `should find existing files at initialization`() {
val bytes = byteArrayOf(101, 102, 103) val bytes = byteArrayOf(101, 102, 103)