Skip to content

Commit d0a77cf

Browse files
committed
CameraScreen: Landscape mode
1 parent b37bca4 commit d0a77cf

File tree

3 files changed

+184
-112
lines changed

3 files changed

+184
-112
lines changed

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

Lines changed: 128 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package org.mydomain.myscan.view
1616

17+
import android.content.res.Configuration
1718
import android.graphics.Bitmap
1819
import android.graphics.BitmapFactory
1920
import android.util.Log
@@ -32,13 +33,16 @@ import androidx.compose.foundation.layout.Arrangement
3233
import androidx.compose.foundation.layout.Box
3334
import androidx.compose.foundation.layout.Column
3435
import androidx.compose.foundation.layout.Row
36+
import androidx.compose.foundation.layout.WindowInsets
3537
import androidx.compose.foundation.layout.fillMaxHeight
3638
import androidx.compose.foundation.layout.fillMaxSize
3739
import androidx.compose.foundation.layout.fillMaxWidth
3840
import androidx.compose.foundation.layout.height
3941
import androidx.compose.foundation.layout.padding
42+
import androidx.compose.foundation.layout.safeDrawing
4043
import androidx.compose.foundation.layout.size
4144
import androidx.compose.foundation.layout.width
45+
import androidx.compose.foundation.layout.windowInsetsPadding
4246
import androidx.compose.foundation.lazy.LazyListState
4347
import androidx.compose.foundation.lazy.rememberLazyListState
4448
import androidx.compose.foundation.shape.CircleShape
@@ -61,6 +65,7 @@ import androidx.compose.runtime.remember
6165
import androidx.compose.runtime.setValue
6266
import androidx.compose.ui.Alignment
6367
import androidx.compose.ui.Modifier
68+
import androidx.compose.ui.draw.rotate
6469
import androidx.compose.ui.geometry.Offset
6570
import androidx.compose.ui.graphics.Color
6671
import androidx.compose.ui.graphics.ImageBitmap
@@ -89,6 +94,7 @@ data class CameraUiState(
8994
val liveAnalysisState: LiveAnalysisState,
9095
val captureState: CaptureState,
9196
val showDetectionError: Boolean,
97+
val isLandscape: Boolean,
9298
val isDebugMode: Boolean
9399
)
94100

@@ -136,6 +142,7 @@ fun CameraScreen(
136142
listState.animateScrollToItem(pageIds.lastIndex)
137143
}
138144
}
145+
val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
139146
CameraScreenScaffold(
140147
cameraPreview = {
141148
CameraPreview(
@@ -144,21 +151,20 @@ fun CameraScreen(
144151
onPreviewViewReady = { view -> previewView = view }
145152
)
146153
},
147-
pageList = {
148-
CommonPageList(
154+
pageListState =
155+
CommonPageListState(
149156
pageIds = pageIds,
150157
imageLoader = { id -> viewModel.getBitmap(id) },
151158
onPageClick = { index -> viewModel.navigateTo(Screen.Document(index)) },
152159
listState = listState,
153-
onLastItemPosition =
154-
{ offset -> thumbnailCoords.value = offset }
155-
)
156-
},
160+
onLastItemPosition = { offset -> thumbnailCoords.value = offset },
161+
),
157162
cameraUiState = CameraUiState(
158163
pageIds.size,
159164
liveAnalysisState,
160165
captureState,
161166
showDetectionError,
167+
isLandscape = isLandscape,
162168
isDebugMode),
163169
onCapture = {
164170
previewView?.bitmap?.let {
@@ -179,58 +185,78 @@ fun CameraScreen(
179185
@Composable
180186
private fun CameraScreenScaffold(
181187
cameraPreview: @Composable () -> Unit,
182-
pageList: @Composable () -> Unit,
188+
pageListState: CommonPageListState,
183189
cameraUiState: CameraUiState,
184190
onCapture: () -> Unit,
185191
onFinalizePressed: () -> Unit,
186192
onDebugModeSwitched: () -> Unit,
187193
thumbnailCoords: MutableState<Offset>,
188194
toAboutScreen: () -> Unit,
189195
) {
196+
val documentBar : @Composable () -> Unit = {
197+
DocumentBar(
198+
pageListState = pageListState,
199+
pageCount = cameraUiState.pageCount,
200+
onFinalizePressed = onFinalizePressed,
201+
onDebugModeSwitched = onDebugModeSwitched,
202+
isLandscape = cameraUiState.isLandscape
203+
)
204+
}
190205
Box {
191-
Scaffold(
192-
bottomBar = {
193-
CameraScreenFooter(
194-
pageList = pageList,
195-
pageCount = cameraUiState.pageCount,
196-
onFinalizePressed = onFinalizePressed,
197-
onDebugModeSwitched = onDebugModeSwitched,
198-
)
206+
if (!cameraUiState.isLandscape) {
207+
Scaffold(
208+
bottomBar = documentBar
209+
) { padding ->
210+
val modifier = Modifier.padding(bottom = padding.calculateBottomPadding()).fillMaxSize()
211+
CameraPreviewBox(cameraPreview, cameraUiState, onCapture, modifier)
199212
}
200-
) { innerPadding ->
201-
Box(
202-
modifier = Modifier
203-
.padding(bottom = innerPadding.calculateBottomPadding())
204-
.fillMaxSize()
205-
) {
206-
CameraPreviewWithOverlay(cameraPreview, cameraUiState, Modifier.align(Alignment.BottomCenter))
207-
Box(
208-
modifier = Modifier
209-
.fillMaxSize()
210-
.padding(innerPadding)
213+
} else {
214+
Scaffold { innerPadding ->
215+
Row(
216+
modifier = Modifier.padding(innerPadding).fillMaxSize()
211217
) {
212-
AboutScreenNavButton(
213-
onClick = toAboutScreen,
214-
modifier = Modifier.align(Alignment.TopEnd)
215-
)
216-
}
217-
if (cameraUiState.isDebugMode) {
218-
MessageBox(cameraUiState.liveAnalysisState.inferenceTime)
218+
CameraPreviewBox(cameraPreview, cameraUiState, onCapture, Modifier)
219+
documentBar()
219220
}
220-
CaptureButton(
221-
onClick = onCapture,
222-
modifier = Modifier
223-
.align(Alignment.BottomCenter)
224-
.padding(16.dp)
225-
)
226221
}
227222
}
223+
AboutScreenNavButton(
224+
onClick = toAboutScreen,
225+
modifier = Modifier.align(Alignment.TopEnd).windowInsetsPadding(WindowInsets.safeDrawing)
226+
)
228227
if (cameraUiState.captureState is CaptureState.CapturePreview) {
229228
CapturedImage(cameraUiState.captureState.processed.asImageBitmap(), thumbnailCoords)
230229
}
231230
}
232231
}
233232

233+
@Composable
234+
private fun CameraPreviewBox(
235+
cameraPreview: @Composable (() -> Unit),
236+
cameraUiState: CameraUiState,
237+
onCapture: () -> Unit,
238+
modifier: Modifier,
239+
) {
240+
Box(
241+
modifier = modifier
242+
) {
243+
CameraPreviewWithOverlay(
244+
cameraPreview,
245+
cameraUiState,
246+
Modifier.align(Alignment.BottomCenter)
247+
)
248+
if (cameraUiState.isDebugMode) {
249+
MessageBox(cameraUiState.liveAnalysisState.inferenceTime)
250+
}
251+
CaptureButton(
252+
onClick = onCapture,
253+
modifier = Modifier
254+
.align(Alignment.BottomCenter)
255+
.padding(16.dp)
256+
)
257+
}
258+
}
259+
234260
@Composable
235261
private fun CapturedImage(image: ImageBitmap, thumbnailCoords: MutableState<Offset>) {
236262
Surface(
@@ -319,8 +345,12 @@ private fun CameraPreviewWithOverlay(
319345
modifier: Modifier,
320346
) {
321347
val captureState = cameraUiState.captureState
322-
val width = LocalConfiguration.current.screenWidthDp
323-
val height = width / 3 * 4
348+
var width = LocalConfiguration.current.screenWidthDp
349+
var height = width * 4 / 3
350+
if (cameraUiState.isLandscape) {
351+
height = LocalConfiguration.current.screenHeightDp
352+
width = height * 4 / 3
353+
}
324354

325355
var showShutter by remember { mutableStateOf(false) }
326356
LaunchedEffect(captureState.frozenImage) {
@@ -366,7 +396,6 @@ private fun CameraPreviewWithOverlay(
366396
)
367397
}
368398
}
369-
370399
}
371400
}
372401

@@ -382,11 +411,12 @@ fun MessageBox(inferenceTime: Long) {
382411
}
383412

384413
@Composable
385-
fun CameraScreenFooter(
386-
pageList: @Composable () -> Unit,
414+
fun DocumentBar(
415+
pageListState: CommonPageListState,
387416
pageCount: Int,
388417
onFinalizePressed: () -> Unit,
389418
onDebugModeSwitched: () -> Unit,
419+
isLandscape: Boolean,
390420
) {
391421
var tapCount by remember { mutableStateOf(0) }
392422
var lastTapTime by remember { mutableStateOf(0L) }
@@ -405,34 +435,55 @@ fun CameraScreenFooter(
405435
lastTapTime = currentTime
406436
}
407437

408-
Column (modifier = Modifier.background(MaterialTheme.colorScheme.surfaceContainer)) {
409-
pageList()
438+
Column (
439+
horizontalAlignment = Alignment.CenterHorizontally,
440+
modifier = Modifier.background(MaterialTheme.colorScheme.surfaceContainer)
441+
) {
442+
CommonPageList(pageListState, Modifier.weight(1f))
410443
BottomAppBar(
411444
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
412445
) {
413-
Row (
414-
modifier = Modifier
415-
.padding(horizontal = 16.dp, vertical = 1.dp)
416-
.fillMaxWidth(),
417-
verticalAlignment = Alignment.CenterVertically,
418-
horizontalArrangement = Arrangement.SpaceBetween
419-
) {
420-
Text(
421-
text = pageCountText(pageCount),
422-
style = MaterialTheme.typography.bodyMedium,
423-
modifier = Modifier.clickable(onClick = onPageCountClick)
424-
)
425-
MainActionButton(
426-
onClick = onFinalizePressed,
427-
enabled = pageCount > 0,
428-
text = "Document",
429-
icon = Icons.AutoMirrored.Filled.Article,
430-
)
446+
if (isLandscape) {
447+
Column(
448+
horizontalAlignment = Alignment.CenterHorizontally,
449+
modifier = Modifier.fillMaxWidth()
450+
) {
451+
Bar(pageCount, onPageCountClick, onFinalizePressed)
452+
}
453+
} else {
454+
Row(
455+
modifier = Modifier
456+
.padding(horizontal = 16.dp, vertical = 1.dp)
457+
.fillMaxWidth(),
458+
verticalAlignment = Alignment.CenterVertically,
459+
horizontalArrangement = Arrangement.SpaceBetween
460+
) {
461+
Bar(pageCount, onPageCountClick, onFinalizePressed)
462+
}
431463
}
432464
}
433465
}
434466
}
435467

468+
@Composable
469+
private fun Bar(
470+
pageCount: Int,
471+
onPageCountClick: () -> Unit,
472+
onFinalizePressed: () -> Unit,
473+
) {
474+
Text(
475+
text = pageCountText(pageCount),
476+
style = MaterialTheme.typography.bodyMedium,
477+
modifier = Modifier.clickable(onClick = onPageCountClick)
478+
)
479+
MainActionButton(
480+
onClick = onFinalizePressed,
481+
enabled = pageCount > 0,
482+
text = "Document",
483+
icon = Icons.AutoMirrored.Filled.Article,
484+
)
485+
}
486+
436487
@Preview(showBackground = true)
437488
@Composable
438489
fun CameraScreenPreview() {
@@ -447,8 +498,14 @@ fun CameraScreenPreviewWithProcessedImage() {
447498
debugImage("gallica.bnf.fr-bpt6k5530456s-1.jpg")))
448499
}
449500

501+
@Preview(showBackground = true, widthDp = 640, heightDp = 320)
450502
@Composable
451-
private fun ScreenPreview(captureState: CaptureState) {
503+
fun CameraScreenPreviewInLandscapeMode() {
504+
ScreenPreview(CaptureState.Idle, rotationDegrees = 90f)
505+
}
506+
507+
@Composable
508+
private fun ScreenPreview(captureState: CaptureState, rotationDegrees: Float = 0f) {
452509
val context = LocalContext.current
453510
MyScanTheme {
454511
val thumbnailCoords = remember { mutableStateOf(Offset.Zero) }
@@ -462,12 +519,13 @@ private fun ScreenPreview(captureState: CaptureState) {
462519
) {
463520
Image(
464521
debugImage("uncropped/img01.jpg").asImageBitmap(),
522+
modifier=Modifier.rotate(rotationDegrees),
465523
contentDescription = null
466524
)
467525
}
468526
},
469-
pageList = {
470-
CommonPageList(
527+
pageListState =
528+
CommonPageListState(
471529
pageIds = listOf(1, 2, 2, 2).map { "gallica.bnf.fr-bpt6k5530456s-$it.jpg" },
472530
imageLoader = { id ->
473531
context.assets.open(id).use { input ->
@@ -476,9 +534,9 @@ private fun ScreenPreview(captureState: CaptureState) {
476534
},
477535
onPageClick = {},
478536
listState = LazyListState(),
479-
)
480-
},
481-
cameraUiState = CameraUiState(pageCount = 4, LiveAnalysisState(), captureState, false, false),
537+
),
538+
cameraUiState = CameraUiState(pageCount = 4, LiveAnalysisState(), captureState,
539+
false, rotationDegrees > 0, false),
482540
onCapture = {},
483541
onFinalizePressed = {},
484542
onDebugModeSwitched = {},

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.fillMaxSize
2929
import androidx.compose.foundation.layout.fillMaxWidth
3030
import androidx.compose.foundation.layout.padding
3131
import androidx.compose.foundation.layout.width
32+
import androidx.compose.foundation.lazy.rememberLazyListState
3233
import androidx.compose.foundation.shape.RoundedCornerShape
3334
import androidx.compose.material.icons.Icons
3435
import androidx.compose.material.icons.filled.Add
@@ -201,10 +202,13 @@ private fun PageList(
201202
) {
202203
Box {
203204
CommonPageList(
204-
pageIds,
205-
imageLoader,
206-
onPageClick = { index -> currentPageIndex.value = index },
207-
currentPageIndex = currentPageIndex.value,
205+
CommonPageListState(
206+
pageIds,
207+
imageLoader,
208+
onPageClick = { index -> currentPageIndex.value = index },
209+
currentPageIndex = currentPageIndex.value,
210+
listState = rememberLazyListState()
211+
)
208212
)
209213
SecondaryActionButton(
210214
icon = Icons.Default.Add,

0 commit comments

Comments
 (0)