Skip to content

Commit ef2a49c

Browse files
committed
CameraScreen: LazyRow with list of pages
1 parent e894c14 commit ef2a49c

File tree

1 file changed

+94
-31
lines changed

1 file changed

+94
-31
lines changed

app/src/main/java/org/mydomain/myscan/view/CameraScreen.kt

Lines changed: 94 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,18 @@ import androidx.compose.foundation.clickable
2727
import androidx.compose.foundation.interaction.MutableInteractionSource
2828
import androidx.compose.foundation.layout.Arrangement
2929
import androidx.compose.foundation.layout.Box
30+
import androidx.compose.foundation.layout.Column
3031
import androidx.compose.foundation.layout.Row
3132
import androidx.compose.foundation.layout.fillMaxSize
3233
import androidx.compose.foundation.layout.fillMaxWidth
3334
import androidx.compose.foundation.layout.height
3435
import androidx.compose.foundation.layout.padding
3536
import androidx.compose.foundation.layout.size
3637
import androidx.compose.foundation.layout.width
38+
import androidx.compose.foundation.lazy.LazyRow
39+
import androidx.compose.foundation.lazy.itemsIndexed
3740
import androidx.compose.foundation.shape.CircleShape
41+
import androidx.compose.foundation.shape.RoundedCornerShape
3842
import androidx.compose.material3.Button
3943
import androidx.compose.material3.MaterialTheme
4044
import androidx.compose.material3.Surface
@@ -48,6 +52,7 @@ import androidx.compose.runtime.remember
4852
import androidx.compose.runtime.setValue
4953
import androidx.compose.ui.Alignment
5054
import androidx.compose.ui.Modifier
55+
import androidx.compose.ui.draw.clip
5156
import androidx.compose.ui.graphics.Color
5257
import androidx.compose.ui.graphics.asImageBitmap
5358
import androidx.compose.ui.platform.LocalConfiguration
@@ -70,6 +75,7 @@ fun CameraScreen(
7075
modifier: Modifier,
7176
) {
7277
var previewView by remember { mutableStateOf<PreviewView?>(null) }
78+
val pageIds by viewModel.pageIds.collectAsStateWithLifecycle()
7379

7480
val captureController = remember { CameraCaptureController() }
7581
DisposableEffect(Unit) {
@@ -92,7 +98,8 @@ fun CameraScreen(
9298
captureController = captureController,
9399
onPreviewViewReady = { view -> previewView = view }
94100
) },
95-
pageCount = viewModel.pageCount(),
101+
pageIds = pageIds,
102+
imageLoader = { id -> viewModel.getBitmap(id) },
96103
liveAnalysisState = liveAnalysisState,
97104
onCapture = {
98105
Log.i("MyScan", "Pressed <Capture>")
@@ -110,7 +117,8 @@ fun CameraScreen(
110117
private fun CameraScreenContent(
111118
modifier: Modifier,
112119
cameraPreview: @Composable () -> Unit,
113-
pageCount: Int,
120+
pageIds: List<String>,
121+
imageLoader: (String) -> Bitmap?,
114122
liveAnalysisState: LiveAnalysisState,
115123
onCapture: () -> Unit,
116124
onFinalizePressed: () -> Unit,
@@ -120,17 +128,20 @@ private fun CameraScreenContent(
120128
CameraPreviewWithOverlay(cameraPreview, liveAnalysisState, captureState)
121129
MessageBox(liveAnalysisState.inferenceTime)
122130

123-
CaptureButton(
124-
onClick = onCapture,
125-
modifier = Modifier
126-
.align(Alignment.BottomCenter)
127-
.padding(bottom = 96.dp)
128-
)
129-
CameraScreenFooter(
130-
pageCount = pageCount,
131-
onFinalizePressed = onFinalizePressed,
132-
modifier = Modifier.align(Alignment.BottomCenter)
133-
)
131+
Column (Modifier.align(Alignment.BottomCenter)) {
132+
CaptureButton(
133+
onClick = onCapture,
134+
modifier = Modifier
135+
.align(Alignment.CenterHorizontally)
136+
.padding(16.dp)
137+
)
138+
CameraScreenFooter(
139+
pageIds = pageIds,
140+
imageLoader = imageLoader,
141+
onFinalizePressed = onFinalizePressed,
142+
modifier = Modifier,
143+
)
144+
}
134145
captureState.processedImage?.let {
135146
Surface (
136147
color = Color.Black.copy(alpha = 0.3f),
@@ -213,37 +224,83 @@ fun MessageBox(inferenceTime: Long) {
213224

214225
@Composable
215226
fun CameraScreenFooter(
216-
pageCount: Int,
227+
pageIds: List<String>,
228+
imageLoader: (String) -> Bitmap?,
217229
onFinalizePressed: () -> Unit,
218230
modifier: Modifier,
219231
) {
232+
val pageCount = pageIds.size
220233
Surface (
221234
color = MaterialTheme.colorScheme.inverseOnSurface,
222235
tonalElevation = 4.dp,
223-
modifier = modifier.fillMaxWidth().height(56.dp)
236+
modifier = modifier
237+
.fillMaxWidth()
238+
.height(180.dp)
224239
) {
225-
Row (
226-
modifier = Modifier
227-
.padding(horizontal = 16.dp, vertical = 8.dp)
228-
.fillMaxWidth(),
229-
verticalAlignment = Alignment.CenterVertically,
230-
horizontalArrangement = Arrangement.SpaceBetween
231-
) {
232-
Text(
233-
text = "Pages : $pageCount",
234-
style = MaterialTheme.typography.bodyMedium
240+
Column {
241+
CameraCapturedPagesRow(
242+
pageIds = pageIds,
243+
imageLoader = imageLoader
235244
)
236-
237-
Button (
238-
onClick = onFinalizePressed,
239-
enabled = pageCount > 0
245+
Row (
246+
modifier = Modifier
247+
.padding(horizontal = 16.dp, vertical = 8.dp)
248+
.fillMaxWidth(),
249+
verticalAlignment = Alignment.CenterVertically,
250+
horizontalArrangement = Arrangement.SpaceBetween
240251
) {
241-
Text("Finish")
252+
Text(
253+
text = "$pageCount pages",
254+
style = MaterialTheme.typography.bodyMedium
255+
)
256+
257+
Button (
258+
onClick = onFinalizePressed,
259+
enabled = pageCount > 0
260+
) {
261+
Text("Finish")
262+
}
263+
}
264+
}
265+
}
266+
}
267+
268+
@Composable
269+
fun CameraCapturedPagesRow(
270+
pageIds: List<String>,
271+
imageLoader: (String) -> Bitmap?
272+
) {
273+
if (pageIds.isEmpty()) return
274+
275+
LazyRow (
276+
modifier = Modifier
277+
.fillMaxWidth()
278+
.padding(horizontal = 8.dp, vertical = 4.dp),
279+
horizontalArrangement = Arrangement.spacedBy(8.dp)
280+
) {
281+
itemsIndexed(pageIds) { index, id ->
282+
val image = imageLoader(id)
283+
if (image != null) {
284+
Box {
285+
val bitmap = image.asImageBitmap()
286+
val modifier =
287+
if (bitmap.height > bitmap.width)
288+
Modifier.height(120.dp)
289+
else
290+
Modifier.width(120.dp)
291+
Image(
292+
bitmap = bitmap,
293+
contentDescription = "Page ${index + 1}",
294+
modifier = modifier
295+
.clip(RoundedCornerShape(4.dp))
296+
)
297+
}
242298
}
243299
}
244300
}
245301
}
246302

303+
247304
@Preview(showBackground = true)
248305
@Composable
249306
fun CameraScreenPreview() {
@@ -258,6 +315,7 @@ fun CameraScreenPreviewWithProcessedImage() {
258315

259316
@Composable
260317
private fun ScreenPreview(captureState: CaptureState) {
318+
val context = LocalContext.current
261319
MyScanTheme {
262320
CameraScreenContent(
263321
modifier = Modifier,
@@ -274,7 +332,12 @@ private fun ScreenPreview(captureState: CaptureState) {
274332
)
275333
}
276334
},
277-
pageCount = 3,
335+
pageIds = listOf(1, 2, 2, 2).map { "gallica.bnf.fr-bpt6k5530456s-$it.jpg" },
336+
imageLoader = { id ->
337+
context.assets.open(id).use { input ->
338+
BitmapFactory.decodeStream(input)
339+
}
340+
},
278341
liveAnalysisState = LiveAnalysisState(),
279342
onCapture = {},
280343
onFinalizePressed = {},

0 commit comments

Comments
 (0)