CameraScreen: LazyRow with list of pages
This commit is contained in:
@@ -27,6 +27,7 @@ import androidx.compose.foundation.clickable
|
|||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -34,7 +35,10 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
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.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
@@ -48,6 +52,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
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.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
@@ -70,6 +75,7 @@ fun CameraScreen(
|
|||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
) {
|
) {
|
||||||
var previewView by remember { mutableStateOf<PreviewView?>(null) }
|
var previewView by remember { mutableStateOf<PreviewView?>(null) }
|
||||||
|
val pageIds by viewModel.pageIds.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val captureController = remember { CameraCaptureController() }
|
val captureController = remember { CameraCaptureController() }
|
||||||
DisposableEffect(Unit) {
|
DisposableEffect(Unit) {
|
||||||
@@ -92,7 +98,8 @@ fun CameraScreen(
|
|||||||
captureController = captureController,
|
captureController = captureController,
|
||||||
onPreviewViewReady = { view -> previewView = view }
|
onPreviewViewReady = { view -> previewView = view }
|
||||||
) },
|
) },
|
||||||
pageCount = viewModel.pageCount(),
|
pageIds = pageIds,
|
||||||
|
imageLoader = { id -> viewModel.getBitmap(id) },
|
||||||
liveAnalysisState = liveAnalysisState,
|
liveAnalysisState = liveAnalysisState,
|
||||||
onCapture = {
|
onCapture = {
|
||||||
Log.i("MyScan", "Pressed <Capture>")
|
Log.i("MyScan", "Pressed <Capture>")
|
||||||
@@ -110,7 +117,8 @@ fun CameraScreen(
|
|||||||
private fun CameraScreenContent(
|
private fun CameraScreenContent(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
cameraPreview: @Composable () -> Unit,
|
cameraPreview: @Composable () -> Unit,
|
||||||
pageCount: Int,
|
pageIds: List<String>,
|
||||||
|
imageLoader: (String) -> Bitmap?,
|
||||||
liveAnalysisState: LiveAnalysisState,
|
liveAnalysisState: LiveAnalysisState,
|
||||||
onCapture: () -> Unit,
|
onCapture: () -> Unit,
|
||||||
onFinalizePressed: () -> Unit,
|
onFinalizePressed: () -> Unit,
|
||||||
@@ -120,17 +128,20 @@ private fun CameraScreenContent(
|
|||||||
CameraPreviewWithOverlay(cameraPreview, liveAnalysisState, captureState)
|
CameraPreviewWithOverlay(cameraPreview, liveAnalysisState, captureState)
|
||||||
MessageBox(liveAnalysisState.inferenceTime)
|
MessageBox(liveAnalysisState.inferenceTime)
|
||||||
|
|
||||||
|
Column (Modifier.align(Alignment.BottomCenter)) {
|
||||||
CaptureButton(
|
CaptureButton(
|
||||||
onClick = onCapture,
|
onClick = onCapture,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.CenterHorizontally)
|
||||||
.padding(bottom = 96.dp)
|
.padding(16.dp)
|
||||||
)
|
)
|
||||||
CameraScreenFooter(
|
CameraScreenFooter(
|
||||||
pageCount = pageCount,
|
pageIds = pageIds,
|
||||||
|
imageLoader = imageLoader,
|
||||||
onFinalizePressed = onFinalizePressed,
|
onFinalizePressed = onFinalizePressed,
|
||||||
modifier = Modifier.align(Alignment.BottomCenter)
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
captureState.processedImage?.let {
|
captureState.processedImage?.let {
|
||||||
Surface (
|
Surface (
|
||||||
color = Color.Black.copy(alpha = 0.3f),
|
color = Color.Black.copy(alpha = 0.3f),
|
||||||
@@ -213,15 +224,24 @@ fun MessageBox(inferenceTime: Long) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CameraScreenFooter(
|
fun CameraScreenFooter(
|
||||||
pageCount: Int,
|
pageIds: List<String>,
|
||||||
|
imageLoader: (String) -> Bitmap?,
|
||||||
onFinalizePressed: () -> Unit,
|
onFinalizePressed: () -> Unit,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
) {
|
) {
|
||||||
|
val pageCount = pageIds.size
|
||||||
Surface (
|
Surface (
|
||||||
color = MaterialTheme.colorScheme.inverseOnSurface,
|
color = MaterialTheme.colorScheme.inverseOnSurface,
|
||||||
tonalElevation = 4.dp,
|
tonalElevation = 4.dp,
|
||||||
modifier = modifier.fillMaxWidth().height(56.dp)
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(180.dp)
|
||||||
) {
|
) {
|
||||||
|
Column {
|
||||||
|
CameraCapturedPagesRow(
|
||||||
|
pageIds = pageIds,
|
||||||
|
imageLoader = imageLoader
|
||||||
|
)
|
||||||
Row (
|
Row (
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
@@ -230,7 +250,7 @@ fun CameraScreenFooter(
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Pages : $pageCount",
|
text = "$pageCount pages",
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -243,6 +263,43 @@ fun CameraScreenFooter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CameraCapturedPagesRow(
|
||||||
|
pageIds: List<String>,
|
||||||
|
imageLoader: (String) -> Bitmap?
|
||||||
|
) {
|
||||||
|
if (pageIds.isEmpty()) return
|
||||||
|
|
||||||
|
LazyRow (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
itemsIndexed(pageIds) { index, id ->
|
||||||
|
val image = imageLoader(id)
|
||||||
|
if (image != null) {
|
||||||
|
Box {
|
||||||
|
val bitmap = image.asImageBitmap()
|
||||||
|
val modifier =
|
||||||
|
if (bitmap.height > bitmap.width)
|
||||||
|
Modifier.height(120.dp)
|
||||||
|
else
|
||||||
|
Modifier.width(120.dp)
|
||||||
|
Image(
|
||||||
|
bitmap = bitmap,
|
||||||
|
contentDescription = "Page ${index + 1}",
|
||||||
|
modifier = modifier
|
||||||
|
.clip(RoundedCornerShape(4.dp))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -258,6 +315,7 @@ fun CameraScreenPreviewWithProcessedImage() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ScreenPreview(captureState: CaptureState) {
|
private fun ScreenPreview(captureState: CaptureState) {
|
||||||
|
val context = LocalContext.current
|
||||||
MyScanTheme {
|
MyScanTheme {
|
||||||
CameraScreenContent(
|
CameraScreenContent(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
@@ -274,7 +332,12 @@ private fun ScreenPreview(captureState: CaptureState) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageCount = 3,
|
pageIds = listOf(1, 2, 2, 2).map { "gallica.bnf.fr-bpt6k5530456s-$it.jpg" },
|
||||||
|
imageLoader = { id ->
|
||||||
|
context.assets.open(id).use { input ->
|
||||||
|
BitmapFactory.decodeStream(input)
|
||||||
|
}
|
||||||
|
},
|
||||||
liveAnalysisState = LiveAnalysisState(),
|
liveAnalysisState = LiveAnalysisState(),
|
||||||
onCapture = {},
|
onCapture = {},
|
||||||
onFinalizePressed = {},
|
onFinalizePressed = {},
|
||||||
|
|||||||
Reference in New Issue
Block a user