Avoid a possible crash when deleting an image

This commit is contained in:
Pierre-Yves Nicolas
2025-06-21 17:34:31 +02:00
parent 60d5bc51ef
commit 8ed04238fb
4 changed files with 46 additions and 40 deletions

View File

@@ -39,12 +39,12 @@ class ImageRepository(appFilesDir: File) {
fileNames.add(fileName)
}
fun getContent(id: String): ByteArray {
fun getContent(id: String): ByteArray? {
if (fileNames.contains(id)) {
val file = File(scanDir, id)
return file.readBytes()
}
throw IllegalArgumentException("No image for id: $id")
return null
}
fun delete(id: String) {

View File

@@ -137,14 +137,15 @@ class MainViewModel(
fun pageCount(): Int = pageIds.value.size
fun getBitmap(id: String): Bitmap {
fun getBitmap(id: String): Bitmap? {
val bytes = imageRepository.getContent(id)
return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
return bytes?.let { BitmapFactory.decodeByteArray(it, 0, it.size) }
}
fun createPdf(outputStream: OutputStream) {
val jpegs = imageRepository.imageIds().asSequence()
.map { id -> imageRepository.getContent(id) }
.filterNotNull()
writePdfFromJpegs(jpegs, outputStream)
}
}

View File

@@ -74,7 +74,7 @@ import org.mydomain.myscan.ui.theme.MyScanTheme
@Composable
fun DocumentScreen(
pageIds: List<String>,
imageLoader: (String) -> Bitmap,
imageLoader: (String) -> Bitmap?,
toCameraScreen: () -> Unit,
onSavePressed: () -> Unit,
onSharePressed: () -> Unit,
@@ -135,7 +135,7 @@ fun DocumentScreen(
@Composable
private fun DocumentPreview(
pageIds: List<String>,
imageLoader: (String) -> Bitmap,
imageLoader: (String) -> Bitmap?,
currentPageIndex: MutableIntState,
onDeleteImage: (String) -> Unit,
padding: PaddingValues,
@@ -151,21 +151,24 @@ private fun DocumentPreview(
modifier = Modifier.fillMaxSize()
) {
val bitmap = imageLoader(imageId)
val imageBitmap = bitmap.asImageBitmap()
val zoomState = rememberZoomState(
contentSize = Size(bitmap.width.toFloat(), bitmap.height.toFloat()))
if (bitmap != null) {
val imageBitmap = bitmap.asImageBitmap()
val zoomState = rememberZoomState(
contentSize = Size(bitmap.width.toFloat(), bitmap.height.toFloat())
)
LaunchedEffect(imageId) {
zoomState.reset()
LaunchedEffect(imageId) {
zoomState.reset()
}
Image(
bitmap = imageBitmap,
contentDescription = null,
modifier = Modifier
.padding(4.dp)
.align(Alignment.Center)
.zoomable(zoomState)
)
}
Image(
bitmap = imageBitmap,
contentDescription = null,
modifier = Modifier
.padding(4.dp)
.align(Alignment.Center)
.zoomable(zoomState)
)
SmallFloatingActionButton(
onClick = { onDeleteImage(imageId) },
modifier = Modifier.align(Alignment.TopEnd).padding(4.dp)
@@ -186,7 +189,7 @@ private fun DocumentPreview(
@Composable
private fun PageList(
pageIds: List<String>,
imageLoader: (String) -> Bitmap,
imageLoader: (String) -> Bitmap?,
currentPageIndex: MutableState<Int>,
toCameraScreen: () -> Unit
) {
@@ -202,22 +205,26 @@ private fun PageList(
) {
itemsIndexed (pageIds) { index, id ->
// TODO Use small images rather than big ones
val bitmap = imageLoader(id).asImageBitmap()
val isSelected = index == currentPageIndex.value
val borderColor = if (isSelected) MaterialTheme.colorScheme.primary else Color.Transparent
val modifier =
if (bitmap.height > bitmap.width)
Modifier.height(120.dp)
else
Modifier.width(120.dp)
Image(
bitmap = bitmap,
contentDescription = null,
modifier = modifier
.padding(4.dp)
.border(2.dp, borderColor)
.clickable { currentPageIndex.value = index }
)
val image = imageLoader(id)
if (image != null) {
val bitmap = image.asImageBitmap()
val isSelected = index == currentPageIndex.value
val borderColor =
if (isSelected) MaterialTheme.colorScheme.primary else Color.Transparent
val modifier =
if (bitmap.height > bitmap.width)
Modifier.height(120.dp)
else
Modifier.width(120.dp)
Image(
bitmap = bitmap,
contentDescription = null,
modifier = modifier
.padding(4.dp)
.border(2.dp, borderColor)
.clickable { currentPageIndex.value = index }
)
}
}
}
SmallFloatingActionButton(

View File

@@ -14,7 +14,6 @@
*/
package org.mydomain.myscan
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
@@ -73,10 +72,9 @@ class ImageRepositoryTest {
}
@Test
fun `should throw on invalid id`() {
fun `should return null on invalid id`() {
val repo = repo()
assertThat(repo.imageIds()).isEmpty()
Assertions.assertThatThrownBy { repo.getContent("x") }
.isInstanceOf(IllegalArgumentException::class.java)
assertThat(repo.getContent("x")).isNull()
}
}