Export screen: add thumbnail
This commit is contained in:
@@ -183,6 +183,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
ExportScreenWrapper(
|
ExportScreenWrapper(
|
||||||
navigation = navigation,
|
navigation = navigation,
|
||||||
uiState = exportUiState,
|
uiState = exportUiState,
|
||||||
|
currentDocument = document,
|
||||||
pdfActions = ExportActions(
|
pdfActions = ExportActions(
|
||||||
initializeExportScreen = exportViewModel::initializeExportScreen,
|
initializeExportScreen = exportViewModel::initializeExportScreen,
|
||||||
setFilename = exportViewModel::setFilename,
|
setFilename = exportViewModel::setFilename,
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import android.text.format.Formatter
|
|||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -30,9 +32,11 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
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.layout.widthIn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||||
@@ -43,6 +47,8 @@ import androidx.compose.material.icons.filled.Download
|
|||||||
import androidx.compose.material.icons.filled.Share
|
import androidx.compose.material.icons.filled.Share
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -65,6 +71,7 @@ import androidx.compose.ui.draw.alpha
|
|||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -74,7 +81,9 @@ import androidx.compose.ui.text.font.FontStyle
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import org.fairscan.app.R
|
import org.fairscan.app.R
|
||||||
|
import org.fairscan.app.THUMBNAIL_SIZE_DP
|
||||||
import org.fairscan.app.ui.Navigation
|
import org.fairscan.app.ui.Navigation
|
||||||
import org.fairscan.app.ui.components.AppOverflowMenu
|
import org.fairscan.app.ui.components.AppOverflowMenu
|
||||||
import org.fairscan.app.ui.components.BackButton
|
import org.fairscan.app.ui.components.BackButton
|
||||||
@@ -83,7 +92,9 @@ import org.fairscan.app.ui.components.NewDocumentDialog
|
|||||||
import org.fairscan.app.ui.components.isLandscape
|
import org.fairscan.app.ui.components.isLandscape
|
||||||
import org.fairscan.app.ui.components.pageCountText
|
import org.fairscan.app.ui.components.pageCountText
|
||||||
import org.fairscan.app.ui.dummyNavigation
|
import org.fairscan.app.ui.dummyNavigation
|
||||||
|
import org.fairscan.app.ui.fakeDocument
|
||||||
import org.fairscan.app.ui.screens.settings.ExportFormat.PDF
|
import org.fairscan.app.ui.screens.settings.ExportFormat.PDF
|
||||||
|
import org.fairscan.app.ui.state.DocumentUiModel
|
||||||
import org.fairscan.app.ui.theme.FairScanTheme
|
import org.fairscan.app.ui.theme.FairScanTheme
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -95,6 +106,7 @@ import java.util.Locale
|
|||||||
fun ExportScreenWrapper(
|
fun ExportScreenWrapper(
|
||||||
navigation: Navigation,
|
navigation: Navigation,
|
||||||
uiState: ExportUiState,
|
uiState: ExportUiState,
|
||||||
|
currentDocument: DocumentUiModel,
|
||||||
pdfActions: ExportActions,
|
pdfActions: ExportActions,
|
||||||
onCloseScan: () -> Unit,
|
onCloseScan: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -122,6 +134,7 @@ fun ExportScreenWrapper(
|
|||||||
filename = filename,
|
filename = filename,
|
||||||
onFilenameChange = onFilenameChange,
|
onFilenameChange = onFilenameChange,
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
|
currentDocument = currentDocument,
|
||||||
navigation = navigation,
|
navigation = navigation,
|
||||||
onShare = {
|
onShare = {
|
||||||
if (!uiState.isSaving) {
|
if (!uiState.isSaving) {
|
||||||
@@ -157,6 +170,7 @@ fun ExportScreen(
|
|||||||
filename: MutableState<String>,
|
filename: MutableState<String>,
|
||||||
onFilenameChange: (String) -> Unit,
|
onFilenameChange: (String) -> Unit,
|
||||||
uiState: ExportUiState,
|
uiState: ExportUiState,
|
||||||
|
currentDocument: DocumentUiModel,
|
||||||
navigation: Navigation,
|
navigation: Navigation,
|
||||||
onShare: () -> Unit,
|
onShare: () -> Unit,
|
||||||
onSave: () -> Unit,
|
onSave: () -> Unit,
|
||||||
@@ -182,7 +196,8 @@ fun ExportScreen(
|
|||||||
modifier = containerModifier.fillMaxSize(),
|
modifier = containerModifier.fillMaxSize(),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
TextFieldAndPdfInfos(filename, onFilenameChange, uiState, onOpen)
|
TextFieldAndPdfInfos(filename, onFilenameChange, uiState, currentDocument, onOpen,
|
||||||
|
onThumbnailClick = navigation.toDocumentScreen)
|
||||||
Spacer(Modifier.weight(1f)) // push buttons down
|
Spacer(Modifier.weight(1f)) // push buttons down
|
||||||
MainActions(uiState, onShare, onSave, onCloseScan)
|
MainActions(uiState, onShare, onSave, onCloseScan)
|
||||||
}
|
}
|
||||||
@@ -195,7 +210,8 @@ fun ExportScreen(
|
|||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
TextFieldAndPdfInfos(filename, onFilenameChange, uiState, onOpen)
|
TextFieldAndPdfInfos(filename, onFilenameChange, uiState, currentDocument, onOpen,
|
||||||
|
onThumbnailClick = navigation.toDocumentScreen)
|
||||||
}
|
}
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
MainActions(uiState, onShare, onSave, onCloseScan)
|
MainActions(uiState, onShare, onSave, onCloseScan)
|
||||||
@@ -211,41 +227,61 @@ private fun TextFieldAndPdfInfos(
|
|||||||
filename: MutableState<String>,
|
filename: MutableState<String>,
|
||||||
onFilenameChange: (String) -> Unit,
|
onFilenameChange: (String) -> Unit,
|
||||||
uiState: ExportUiState,
|
uiState: ExportUiState,
|
||||||
|
currentDocument: DocumentUiModel,
|
||||||
onOpen: (SavedItem) -> Unit,
|
onOpen: (SavedItem) -> Unit,
|
||||||
|
onThumbnailClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
FilenameTextField(filename, onFilenameChange)
|
FilenameTextField(filename, onFilenameChange)
|
||||||
|
|
||||||
val result = uiState.result
|
val result = uiState.result
|
||||||
|
|
||||||
// PDF infos
|
Row (horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||||
Column(
|
val thumbnail = currentDocument.loadThumbnail(0)
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
thumbnail?.let {
|
||||||
) {
|
Card(
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
|
||||||
if (uiState.isGenerating) {
|
shape = RoundedCornerShape(6.dp),
|
||||||
Row(
|
modifier = Modifier.padding(4.dp)
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
.heightIn(max = THUMBNAIL_SIZE_DP.dp)
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
.widthIn(max = THUMBNAIL_SIZE_DP.dp)
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(
|
Image(
|
||||||
modifier = Modifier.size(16.dp),
|
bitmap = thumbnail.asImageBitmap(),
|
||||||
strokeWidth = 2.dp
|
contentDescription = null,
|
||||||
)
|
modifier = Modifier.clickable { onThumbnailClick() }
|
||||||
Text(
|
)
|
||||||
text = stringResource(R.string.creating_export),
|
}
|
||||||
fontStyle = FontStyle.Italic
|
}
|
||||||
|
// PDF infos
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (uiState.isGenerating) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
strokeWidth = 2.dp
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.creating_export),
|
||||||
|
fontStyle = FontStyle.Italic
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (result != null) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val formattedFileSize = formatFileSize(result.sizeInBytes, context)
|
||||||
|
Text(text = pageCountText(result.pageCount))
|
||||||
|
val sizeMessageKey =
|
||||||
|
if (result.files.size == 1) R.string.file_size else R.string.file_size_total
|
||||||
|
Text(
|
||||||
|
text = stringResource(sizeMessageKey, formattedFileSize),
|
||||||
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (result != null) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val formattedFileSize = formatFileSize(result.sizeInBytes, context)
|
|
||||||
Text(text = pageCountText(result.pageCount))
|
|
||||||
val sizeMessageKey =
|
|
||||||
if (result.files.size == 1) R.string.file_size else R.string.file_size_total
|
|
||||||
Text(
|
|
||||||
text = stringResource(sizeMessageKey, formattedFileSize),
|
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SaveStatusBar(uiState, onOpen)
|
SaveStatusBar(uiState, onOpen)
|
||||||
@@ -609,8 +645,12 @@ fun ExportPreviewToCustomize(uiState: ExportUiState) {
|
|||||||
ExportScreen(
|
ExportScreen(
|
||||||
filename = remember { mutableStateOf("Scan 2025-07-02 17.40.42") },
|
filename = remember { mutableStateOf("Scan 2025-07-02 17.40.42") },
|
||||||
onFilenameChange = {_->},
|
onFilenameChange = {_->},
|
||||||
navigation = dummyNavigation(),
|
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
|
currentDocument = fakeDocument(
|
||||||
|
persistentListOf("gallica.bnf.fr-bpt6k5530456s-1"),
|
||||||
|
LocalContext.current
|
||||||
|
),
|
||||||
|
navigation = dummyNavigation(),
|
||||||
onShare = {},
|
onShare = {},
|
||||||
onSave = {},
|
onSave = {},
|
||||||
onOpen = {},
|
onOpen = {},
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ fun HomeScreenPreviewWithCurrentDocument() {
|
|||||||
HomeScreen(
|
HomeScreen(
|
||||||
cameraPermission = rememberCameraPermissionState(),
|
cameraPermission = rememberCameraPermissionState(),
|
||||||
currentDocument = fakeDocument(
|
currentDocument = fakeDocument(
|
||||||
persistentListOf("gallica.bnf.fr-bpt6k5530456s-1.jpg"),
|
persistentListOf("gallica.bnf.fr-bpt6k5530456s-1"),
|
||||||
LocalContext.current
|
LocalContext.current
|
||||||
),
|
),
|
||||||
navigation = dummyNavigation(),
|
navigation = dummyNavigation(),
|
||||||
|
|||||||
Reference in New Issue
Block a user