@@ -42,6 +42,7 @@ import androidx.compose.foundation.layout.width
4242import androidx.compose.foundation.lazy.LazyListState
4343import androidx.compose.foundation.lazy.rememberLazyListState
4444import androidx.compose.foundation.shape.CircleShape
45+ import androidx.compose.foundation.shape.RoundedCornerShape
4546import androidx.compose.material3.BottomAppBar
4647import androidx.compose.material3.Button
4748import androidx.compose.material3.MaterialTheme
@@ -71,6 +72,7 @@ import androidx.compose.ui.platform.LocalContext
7172import androidx.compose.ui.platform.LocalDensity
7273import androidx.compose.ui.tooling.preview.Preview
7374import androidx.compose.ui.unit.dp
75+ import androidx.compose.ui.unit.sp
7476import androidx.lifecycle.compose.collectAsStateWithLifecycle
7577import kotlinx.coroutines.delay
7678import org.mydomain.myscan.LiveAnalysisState
@@ -105,13 +107,23 @@ fun CameraScreen(
105107 }
106108
107109 val captureState by viewModel.captureState.collectAsStateWithLifecycle()
108- if (captureState.isProcessed() ) {
110+ if (captureState is CaptureState . CapturePreview ) {
109111 LaunchedEffect (captureState) {
110112 delay(CAPTURED_IMAGE_DISPLAY_DURATION )
111113 viewModel.addProcessedImage()
112114 }
113115 }
114116
117+ val showDetectionError = remember { mutableStateOf(false ) }
118+ LaunchedEffect (captureState) {
119+ if (captureState is CaptureState .CaptureError ) {
120+ showDetectionError.value = true
121+ delay(1000 )
122+ showDetectionError.value = false
123+ viewModel.afterCaptureError()
124+ }
125+ }
126+
115127 val listState = rememberLazyListState()
116128 LaunchedEffect (pageIds.size) {
117129 if (pageIds.isNotEmpty()) {
@@ -138,14 +150,17 @@ fun CameraScreen(
138150 },
139151 cameraUiState = CameraUiState (pageIds.size, liveAnalysisState, captureState),
140152 onCapture = {
141- Log .i(" MyScan" , " Pressed <Capture>" )
142- viewModel.onCapturePressed(previewView?.bitmap)
143- captureController.takePicture(
144- onImageCaptured = { imageProxy -> viewModel.onImageCaptured(imageProxy) }
145- )
153+ previewView?.bitmap?.let {
154+ Log .i(" MyScan" , " Pressed <Capture>" )
155+ viewModel.onCapturePressed(it)
156+ captureController.takePicture(
157+ onImageCaptured = { imageProxy -> viewModel.onImageCaptured(imageProxy) }
158+ )
159+ }
146160 },
147161 onFinalizePressed = onFinalizePressed,
148162 thumbnailCoords = thumbnailCoords,
163+ showDetectionError = showDetectionError.value
149164 )
150165}
151166
@@ -157,6 +172,7 @@ private fun CameraScreenScaffold(
157172 onCapture : () -> Unit ,
158173 onFinalizePressed : () -> Unit ,
159174 thumbnailCoords : MutableState <Offset >,
175+ showDetectionError : Boolean ,
160176) {
161177 Box {
162178 Scaffold (
@@ -173,7 +189,7 @@ private fun CameraScreenScaffold(
173189 .padding(bottom = innerPadding.calculateBottomPadding())
174190 .fillMaxSize()
175191 ) {
176- CameraPreviewWithOverlay (cameraPreview, cameraUiState)
192+ CameraPreviewWithOverlay (cameraPreview, cameraUiState, showDetectionError )
177193 MessageBox (cameraUiState.liveAnalysisState.inferenceTime)
178194 CaptureButton (
179195 onClick = onCapture,
@@ -183,8 +199,8 @@ private fun CameraScreenScaffold(
183199 )
184200 }
185201 }
186- cameraUiState.captureState.processedImage?. let {
187- image -> CapturedImage (image .asImageBitmap(), thumbnailCoords)
202+ if ( cameraUiState.captureState is CaptureState . CapturePreview ) {
203+ CapturedImage (cameraUiState.captureState.processed .asImageBitmap(), thumbnailCoords)
188204 }
189205 }
190206}
@@ -273,14 +289,16 @@ fun CaptureButton(onClick: () -> Unit, modifier: Modifier) {
273289@Composable
274290private fun CameraPreviewWithOverlay (
275291 cameraPreview : @Composable () -> Unit ,
276- cameraUiState : CameraUiState
292+ cameraUiState : CameraUiState ,
293+ showDetectionError : Boolean
277294) {
295+ val captureState = cameraUiState.captureState
278296 val width = LocalConfiguration .current.screenWidthDp
279297 val height = width / 3 * 4
280298
281299 var showShutter by remember { mutableStateOf(false ) }
282- LaunchedEffect (cameraUiState. captureState.frozenImage) {
283- if (cameraUiState. captureState.frozenImage != null ) {
300+ LaunchedEffect (captureState.frozenImage) {
301+ if (captureState.frozenImage != null ) {
284302 showShutter = true
285303 delay(200 )
286304 showShutter = false
@@ -294,7 +312,7 @@ private fun CameraPreviewWithOverlay(
294312 ) {
295313 cameraPreview()
296314 AnalysisOverlay (cameraUiState.liveAnalysisState)
297- cameraUiState. captureState.frozenImage?.let {
315+ captureState.frozenImage?.let {
298316 Image (
299317 bitmap = it.asImageBitmap(),
300318 contentDescription = null ,
@@ -308,6 +326,21 @@ private fun CameraPreviewWithOverlay(
308326 .background(Color .Black .copy(alpha = 0.6f ))
309327 )
310328 }
329+ if (showDetectionError) {
330+ Box (
331+ modifier = Modifier
332+ .align(Alignment .Center )
333+ .background(Color .Black .copy(alpha = 0.7f ), shape = RoundedCornerShape (8 .dp))
334+ .padding(16 .dp)
335+ ) {
336+ Text (
337+ text = " No document detected" ,
338+ color = Color .White ,
339+ fontSize = 16 .sp
340+ )
341+ }
342+ }
343+
311344 }
312345}
313346
@@ -359,13 +392,15 @@ fun CameraScreenFooter(
359392@Preview(showBackground = true )
360393@Composable
361394fun CameraScreenPreview () {
362- ScreenPreview (CaptureState () )
395+ ScreenPreview (CaptureState . Idle )
363396}
364397
365398@Preview(showBackground = true , showSystemUi = true )
366399@Composable
367400fun CameraScreenPreviewWithProcessedImage () {
368- ScreenPreview (CaptureState (processedImage = debugImage(" gallica.bnf.fr-bpt6k5530456s-1.jpg" )))
401+ ScreenPreview (CaptureState .CapturePreview (
402+ debugImage(" uncropped/img01.jpg" ),
403+ debugImage(" gallica.bnf.fr-bpt6k5530456s-1.jpg" )))
369404}
370405
371406@Composable
@@ -403,6 +438,7 @@ private fun ScreenPreview(captureState: CaptureState) {
403438 onCapture = {},
404439 onFinalizePressed = {},
405440 thumbnailCoords = thumbnailCoords,
441+ showDetectionError = false ,
406442 )
407443 }
408444}
0 commit comments