diff --git a/app/src/main/java/org/mydomain/myscan/ImageRepository.kt b/app/src/main/java/org/mydomain/myscan/ImageRepository.kt index da88dc7..882e3e1 100644 --- a/app/src/main/java/org/mydomain/myscan/ImageRepository.kt +++ b/app/src/main/java/org/mydomain/myscan/ImageRepository.kt @@ -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) { diff --git a/app/src/main/java/org/mydomain/myscan/MainViewModel.kt b/app/src/main/java/org/mydomain/myscan/MainViewModel.kt index 479c58b..78383e3 100644 --- a/app/src/main/java/org/mydomain/myscan/MainViewModel.kt +++ b/app/src/main/java/org/mydomain/myscan/MainViewModel.kt @@ -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) } } diff --git a/app/src/main/java/org/mydomain/myscan/view/DocumentScreen.kt b/app/src/main/java/org/mydomain/myscan/view/DocumentScreen.kt index f0b9bcb..8ad0cb3 100644 --- a/app/src/main/java/org/mydomain/myscan/view/DocumentScreen.kt +++ b/app/src/main/java/org/mydomain/myscan/view/DocumentScreen.kt @@ -74,7 +74,7 @@ import org.mydomain.myscan.ui.theme.MyScanTheme @Composable fun DocumentScreen( pageIds: List, - imageLoader: (String) -> Bitmap, + imageLoader: (String) -> Bitmap?, toCameraScreen: () -> Unit, onSavePressed: () -> Unit, onSharePressed: () -> Unit, @@ -135,7 +135,7 @@ fun DocumentScreen( @Composable private fun DocumentPreview( pageIds: List, - 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, - imageLoader: (String) -> Bitmap, + imageLoader: (String) -> Bitmap?, currentPageIndex: MutableState, 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( diff --git a/app/src/test/java/org/mydomain/myscan/ImageRepositoryTest.kt b/app/src/test/java/org/mydomain/myscan/ImageRepositoryTest.kt index de3b64f..0b81e6a 100644 --- a/app/src/test/java/org/mydomain/myscan/ImageRepositoryTest.kt +++ b/app/src/test/java/org/mydomain/myscan/ImageRepositoryTest.kt @@ -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() } } \ No newline at end of file