Multi-page step 1: PageValidationDialog
This commit is contained in:
@@ -3,7 +3,6 @@ package org.mydomain.myscan
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
import android.util.Log
|
|
||||||
import androidx.camera.core.ImageProxy
|
import androidx.camera.core.ImageProxy
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@@ -68,13 +67,10 @@ class MainViewModel(private val imageSegmentationService: ImageSegmentationServi
|
|||||||
_currentScreen.value = screen
|
_currentScreen.value = screen
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processCapturedImageAndNavigate(imageProxy: ImageProxy) {
|
fun processCapturedImageThen(imageProxy: ImageProxy, onResult: (Bitmap?) -> Unit) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
Log.d("MyScan", "Navigating to spinner")
|
|
||||||
navigateTo(Screen.PagePreview(image = null, isProcessing = true))
|
|
||||||
val processedImage = processCapturedImage(imageProxy)
|
val processedImage = processCapturedImage(imageProxy)
|
||||||
Log.d("MyScan", "Navigating to result image")
|
onResult(processedImage)
|
||||||
navigateTo(Screen.PagePreview(image = processedImage, isProcessing = false))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,4 +94,8 @@ class MainViewModel(private val imageSegmentationService: ImageSegmentationServi
|
|||||||
val matrix = Matrix().apply { postRotate(degrees.toFloat()) }
|
val matrix = Matrix().apply { postRotate(degrees.toFloat()) }
|
||||||
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
|
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fun addPage(bitmap: Bitmap) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ fun CameraScreen(
|
|||||||
uiState: CameraScreenState,
|
uiState: CameraScreenState,
|
||||||
onImageAnalyzed: (ImageProxy) -> Unit,
|
onImageAnalyzed: (ImageProxy) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
// TODO Should we move those variables to ViewModel?
|
||||||
|
// TODO pause the live analysis when displaying the PageValidationDialogs
|
||||||
|
val showPageDialog = remember { mutableStateOf(false) }
|
||||||
|
val isProcessing = remember { mutableStateOf(false) }
|
||||||
|
val processedPage = remember { mutableStateOf<Bitmap?>(null) }
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val requestPermissionLauncher = rememberLauncherForActivityResult(
|
val requestPermissionLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.RequestPermission()
|
ActivityResultContracts.RequestPermission()
|
||||||
@@ -101,10 +107,15 @@ fun CameraScreen(
|
|||||||
MessageBox(uiState.inferenceTime)
|
MessageBox(uiState.inferenceTime)
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
showPageDialog.value = true
|
||||||
|
isProcessing.value = true
|
||||||
captureController.takePicture(
|
captureController.takePicture(
|
||||||
onImageCaptured = { imageProxy ->
|
onImageCaptured = { imageProxy ->
|
||||||
if (imageProxy != null) {
|
if (imageProxy != null) {
|
||||||
viewModel.processCapturedImageAndNavigate(imageProxy)
|
viewModel.processCapturedImageThen(imageProxy) { result ->
|
||||||
|
processedPage.value = result
|
||||||
|
isProcessing.value = false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e("MyScan", "Error during image capture")
|
Log.e("MyScan", "Error during image capture")
|
||||||
}
|
}
|
||||||
@@ -115,6 +126,23 @@ fun CameraScreen(
|
|||||||
Text("Capture")
|
Text("Capture")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showPageDialog.value) {
|
||||||
|
PageValidationDialog(
|
||||||
|
isProcessing = isProcessing.value,
|
||||||
|
pageBitmap = processedPage.value,
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.addPage(processedPage.value!!)
|
||||||
|
showPageDialog.value = false
|
||||||
|
},
|
||||||
|
onReject = {
|
||||||
|
showPageDialog.value = false
|
||||||
|
},
|
||||||
|
onDismiss = {
|
||||||
|
showPageDialog.value = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
84
app/src/main/java/org/mydomain/myscan/view/PageValidation.kt
Normal file
84
app/src/main/java/org/mydomain/myscan/view/PageValidation.kt
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package org.mydomain.myscan.view
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PageValidationDialog(
|
||||||
|
isProcessing: Boolean,
|
||||||
|
pageBitmap: Bitmap?,
|
||||||
|
onConfirm: () -> Unit,
|
||||||
|
onReject: () -> Unit,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
Dialog (onDismissRequest = onDismiss) {
|
||||||
|
Surface (
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
color = MaterialTheme.colorScheme.surface,
|
||||||
|
tonalElevation = 8.dp,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.aspectRatio(3f / 4f)
|
||||||
|
) {
|
||||||
|
if (isProcessing) {
|
||||||
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Column (
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
if (pageBitmap == null) {
|
||||||
|
Text("Failed to process image")
|
||||||
|
} else {
|
||||||
|
Image(
|
||||||
|
bitmap = pageBitmap.asImageBitmap(),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f),
|
||||||
|
contentScale = ContentScale.Fit
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row (
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
OutlinedButton (onClick = onReject) {
|
||||||
|
Text("Reject")
|
||||||
|
}
|
||||||
|
Button (onClick = onConfirm) {
|
||||||
|
Text("OK")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user