PDF generation: complete the new system
Use the chosen filename, fix errors on sharing, save on a separate thread
This commit is contained in:
8
.idea/deploymentTargetSelector.xml
generated
8
.idea/deploymentTargetSelector.xml
generated
@@ -15,6 +15,14 @@
|
|||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="MainActivity">
|
<SelectionState runConfigName="MainActivity">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2025-07-03T06:58:49.513127900Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=99021FFAZ009KN" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
package org.mydomain.myscan
|
package org.mydomain.myscan
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -29,6 +29,11 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.mydomain.myscan.ui.theme.MyScanTheme
|
import org.mydomain.myscan.ui.theme.MyScanTheme
|
||||||
import org.mydomain.myscan.view.CameraScreen
|
import org.mydomain.myscan.view.CameraScreen
|
||||||
import org.mydomain.myscan.view.DocumentScreen
|
import org.mydomain.myscan.view.DocumentScreen
|
||||||
@@ -62,12 +67,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
initialPage = screen.initialPage,
|
initialPage = screen.initialPage,
|
||||||
imageLoader = { id -> viewModel.getBitmap(id) },
|
imageLoader = { id -> viewModel.getBitmap(id) },
|
||||||
toCameraScreen = { viewModel.navigateTo(Screen.Camera) },
|
toCameraScreen = { viewModel.navigateTo(Screen.Camera) },
|
||||||
// TODO Save and share files with the filename chosen by the user
|
|
||||||
pdfActions = PdfGenerationActions(
|
pdfActions = PdfGenerationActions(
|
||||||
generatePdf = viewModel::generatePdf,
|
startGeneration = viewModel::startPdfGeneration,
|
||||||
onShare = { uri -> sharePdf(uri) },
|
cancelGeneration = viewModel::cancelPdfGeneration,
|
||||||
onSave = { uri -> savePdf(uri) },
|
setFilename = viewModel::setFilename,
|
||||||
onOpen = { uri -> savePdf(uri) /* TODO Open */}
|
generatedPdfFlow = viewModel.generatedPdf,
|
||||||
|
sharePdf = { sharePdf(viewModel.getFinalPdf()) },
|
||||||
|
savePdf = { savePdf(viewModel.getFinalPdf()) },
|
||||||
),
|
),
|
||||||
onStartNew = {
|
onStartNew = {
|
||||||
viewModel.startNewDocument()
|
viewModel.startNewDocument()
|
||||||
@@ -80,32 +86,63 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sharePdf(fileUri: Uri) {
|
private fun sharePdf(generatedPdf: GeneratedPdf?) {
|
||||||
val fileUri = FileProvider.getUriForFile(
|
if (generatedPdf == null)
|
||||||
this,
|
return
|
||||||
"${applicationContext.packageName}.fileprovider",
|
val file = generatedPdf.uri.toFile()
|
||||||
fileUri.toFile()
|
val authority = "${applicationContext.packageName}.fileprovider"
|
||||||
)
|
val fileUri = FileProvider.getUriForFile(this, authority, file)
|
||||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||||
type = "application/pdf"
|
type = "application/pdf"
|
||||||
putExtra(Intent.EXTRA_STREAM, fileUri)
|
putExtra(Intent.EXTRA_STREAM, fileUri)
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
startActivity(Intent.createChooser(shareIntent, "Share PDF"))
|
|
||||||
|
val chooser = Intent.createChooser(shareIntent, "Share PDF")
|
||||||
|
val resInfoList = packageManager.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
|
for (resInfo in resInfoList) {
|
||||||
|
val packageName = resInfo.activityInfo.packageName
|
||||||
|
grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
startActivity(chooser)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun savePdf(fileUri: Uri) {
|
private fun savePdf(generatedPdf: GeneratedPdf?) {
|
||||||
val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
if (generatedPdf == null)
|
||||||
if (!downloadsDir.exists()) {
|
return
|
||||||
downloadsDir.mkdirs()
|
val appScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
val context = this
|
||||||
|
appScope.launch {
|
||||||
|
try {
|
||||||
|
val downloadsDir =
|
||||||
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||||
|
if (!downloadsDir.exists()) {
|
||||||
|
downloadsDir.mkdirs()
|
||||||
|
}
|
||||||
|
val generatedFile = generatedPdf.uri.toFile()
|
||||||
|
val targetFile = File(downloadsDir, generatedFile.name)
|
||||||
|
// TODO Handle case where the target file already exists (choose a unique name)
|
||||||
|
generatedFile.copyTo(targetFile)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
// TODO Display a link to the file
|
||||||
|
Toast.makeText(context, "Saved PDF in Downloads", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
MediaScannerConnection.scanFile(
|
||||||
|
context,
|
||||||
|
arrayOf(targetFile.absolutePath),
|
||||||
|
arrayOf("application/pdf")
|
||||||
|
) { _, _ -> continuation.resume(Unit) {} }
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("MyScan", "Failed to save PDF", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, "Failed to save PDF", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val generatedFile = fileUri.toFile()
|
|
||||||
val targetFile = File(downloadsDir, generatedFile.name)
|
|
||||||
generatedFile.copyTo(targetFile)
|
|
||||||
MediaScannerConnection.scanFile(
|
|
||||||
this, arrayOf(targetFile.absolutePath), arrayOf("application/pdf"), null
|
|
||||||
)
|
|
||||||
Toast.makeText(this, "Saved PDF in Downloads", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initLibraries() {
|
private fun initLibraries() {
|
||||||
|
|||||||
@@ -19,12 +19,14 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.camera.core.ImageProxy
|
import androidx.camera.core.ImageProxy
|
||||||
|
import androidx.core.net.toFile
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@@ -35,7 +37,6 @@ import kotlinx.coroutines.withContext
|
|||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
class MainViewModel(
|
class MainViewModel(
|
||||||
private val imageSegmentationService: ImageSegmentationService,
|
private val imageSegmentationService: ImageSegmentationService,
|
||||||
@@ -194,20 +195,52 @@ class MainViewModel(
|
|||||||
return bytes?.let { BitmapFactory.decodeByteArray(it, 0, it.size) }
|
return bytes?.let { BitmapFactory.decodeByteArray(it, 0, it.size) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createPdf(outputStream: OutputStream) {
|
private suspend fun generatePdf(): GeneratedPdf = withContext(Dispatchers.IO) {
|
||||||
val jpegs = imageRepository.imageIds().asSequence()
|
val imageIds = imageRepository.imageIds()
|
||||||
|
val file = File(pdfDir, "${System.currentTimeMillis()}.pdf")
|
||||||
|
val jpegs = imageIds.asSequence()
|
||||||
.map { id -> imageRepository.getContent(id) }
|
.map { id -> imageRepository.getContent(id) }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
writePdfFromJpegs(jpegs, outputStream)
|
writePdfFromJpegs(jpegs, FileOutputStream(file))
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun generatePdf(): GeneratedPdf = withContext(Dispatchers.IO) {
|
|
||||||
val pageCount = imageRepository.imageIds().size
|
|
||||||
val file = File(pdfDir,"${System.currentTimeMillis()}.pdf")
|
|
||||||
createPdf(FileOutputStream(file))
|
|
||||||
val sizeBytes = file.length()
|
val sizeBytes = file.length()
|
||||||
val uri = file.toUri()
|
val uri = file.toUri()
|
||||||
return@withContext GeneratedPdf(uri, sizeBytes, pageCount)
|
return@withContext GeneratedPdf(uri, sizeBytes, imageIds.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _generatedPdf = MutableStateFlow<GeneratedPdf?>(null)
|
||||||
|
val generatedPdf: StateFlow<GeneratedPdf?> = _generatedPdf
|
||||||
|
|
||||||
|
private var generationJob: Job? = null
|
||||||
|
private var desiredFilename: String = ""
|
||||||
|
|
||||||
|
fun setFilename(name: String) {
|
||||||
|
desiredFilename = name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startPdfGeneration() {
|
||||||
|
if (_generatedPdf.value != null) return
|
||||||
|
generationJob = viewModelScope.launch {
|
||||||
|
val result = generatePdf()
|
||||||
|
_generatedPdf.value = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelPdfGeneration() {
|
||||||
|
generationJob?.cancel()
|
||||||
|
_generatedPdf.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFinalPdf(): GeneratedPdf? {
|
||||||
|
val temp = _generatedPdf.value ?: return null
|
||||||
|
val tempFile = temp.uri.toFile()
|
||||||
|
val newFile = File(tempFile.parentFile, desiredFilename)
|
||||||
|
if (tempFile.absolutePath != newFile.absolutePath) {
|
||||||
|
if (newFile.exists()) newFile.delete()
|
||||||
|
val success = tempFile.renameTo(newFile)
|
||||||
|
if (!success) return null
|
||||||
|
_generatedPdf.value = GeneratedPdf(uri = newFile.toUri(), temp.sizeInBytes, temp.pageCount)
|
||||||
|
}
|
||||||
|
return _generatedPdf.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,8 +251,10 @@ data class GeneratedPdf(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class PdfGenerationActions(
|
data class PdfGenerationActions(
|
||||||
val generatePdf: suspend () -> GeneratedPdf?,
|
val startGeneration: () -> Unit,
|
||||||
val onShare: (Uri) -> Unit,
|
val cancelGeneration: () -> Unit,
|
||||||
val onSave: (Uri) -> Unit,
|
val setFilename: (String) -> Unit,
|
||||||
val onOpen: (Uri) -> Unit
|
val generatedPdfFlow: StateFlow<GeneratedPdf?>,
|
||||||
|
val sharePdf: () -> Unit,
|
||||||
|
val savePdf: () -> Unit
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
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 kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import net.engawapg.lib.zoomable.rememberZoomState
|
import net.engawapg.lib.zoomable.rememberZoomState
|
||||||
import net.engawapg.lib.zoomable.zoomable
|
import net.engawapg.lib.zoomable.zoomable
|
||||||
import org.mydomain.myscan.PdfGenerationActions
|
import org.mydomain.myscan.PdfGenerationActions
|
||||||
@@ -289,7 +290,10 @@ fun DocumentScreenPreview() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
toCameraScreen = {},
|
toCameraScreen = {},
|
||||||
pdfActions = PdfGenerationActions({ null }, {}, {}, {}),
|
pdfActions = PdfGenerationActions(
|
||||||
|
{}, {}, {},
|
||||||
|
MutableStateFlow(null),
|
||||||
|
{}, {}),
|
||||||
onStartNew = {},
|
onStartNew = {},
|
||||||
onDeleteImage = { _ -> {} }
|
onDeleteImage = { _ -> {} }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
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
|
||||||
@@ -41,9 +41,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
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.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.mydomain.myscan.GeneratedPdf
|
import org.mydomain.myscan.GeneratedPdf
|
||||||
import org.mydomain.myscan.PdfGenerationActions
|
import org.mydomain.myscan.PdfGenerationActions
|
||||||
import org.mydomain.myscan.ui.theme.MyScanTheme
|
import org.mydomain.myscan.ui.theme.MyScanTheme
|
||||||
@@ -57,52 +54,37 @@ fun PdfGenerationDialogWrapper(
|
|||||||
pdfActions: PdfGenerationActions,
|
pdfActions: PdfGenerationActions,
|
||||||
) {
|
) {
|
||||||
var filename by remember { mutableStateOf(defaultFilename()) }
|
var filename by remember { mutableStateOf(defaultFilename()) }
|
||||||
var isGenerating by remember { mutableStateOf(true) }
|
val generatedPdf by pdfActions.generatedPdfFlow.collectAsState()
|
||||||
var pdf by remember { mutableStateOf<GeneratedPdf?>(null) }
|
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
var job by remember { mutableStateOf<Job?>(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
job = coroutineScope.launch {
|
pdfActions.setFilename(filename)
|
||||||
try {
|
pdfActions.startGeneration()
|
||||||
val result = pdfActions.generatePdf()
|
|
||||||
pdf = result
|
|
||||||
isGenerating = false
|
|
||||||
} catch (_: CancellationException) {
|
|
||||||
// Cancelled
|
|
||||||
} catch (_: Exception) {
|
|
||||||
// Error
|
|
||||||
isGenerating = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PdfGenerationDialog(
|
PdfGenerationDialog(
|
||||||
filename = filename,
|
filename = filename,
|
||||||
onFilenameChange = { filename = it },
|
onFilenameChange = {
|
||||||
isGenerating = isGenerating,
|
filename = it
|
||||||
pdf = pdf,
|
pdfActions.setFilename(it)
|
||||||
|
},
|
||||||
|
pdf = generatedPdf,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
job?.cancel()
|
pdfActions.cancelGeneration()
|
||||||
onDismiss()
|
onDismiss()
|
||||||
},
|
},
|
||||||
onShare = { pdf?.uri?.let(pdfActions.onShare) },
|
onShare = { pdfActions.sharePdf() },
|
||||||
onSave = { pdf?.uri?.let(pdfActions.onSave) },
|
onSave = { pdfActions.savePdf() },
|
||||||
onOpen = { pdf?.uri?.let(pdfActions.onOpen) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Handle error in PDF generation
|
||||||
@Composable
|
@Composable
|
||||||
fun PdfGenerationDialog(
|
fun PdfGenerationDialog(
|
||||||
filename: String,
|
filename: String,
|
||||||
onFilenameChange: (String) -> Unit,
|
onFilenameChange: (String) -> Unit,
|
||||||
isGenerating: Boolean,
|
|
||||||
pdf: GeneratedPdf?,
|
pdf: GeneratedPdf?,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onShare: () -> Unit,
|
onShare: () -> Unit,
|
||||||
onSave: () -> Unit,
|
onSave: () -> Unit,
|
||||||
onOpen: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
@@ -115,7 +97,7 @@ fun PdfGenerationDialog(
|
|||||||
label = { Text("Filename") },
|
label = { Text("Filename") },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
if (isGenerating) {
|
if (pdf == null) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
modifier = Modifier.size(24.dp),
|
modifier = Modifier.size(24.dp),
|
||||||
@@ -124,19 +106,17 @@ fun PdfGenerationDialog(
|
|||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
Text("Generating PDF…")
|
Text("Generating PDF…")
|
||||||
}
|
}
|
||||||
} else if (pdf != null) {
|
} else {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
Text("${pdf.pageCount} pages – ${formatFileSize(pdf.sizeInBytes, context)}")
|
Text("${pdf.pageCount} pages – ${formatFileSize(pdf.sizeInBytes, context)}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO 4 buttons (counting dismissButton): that's too many
|
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
if (!isGenerating && pdf != null) {
|
if (pdf != null) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
TextButton(onClick = onShare) { Text("Share") }
|
TextButton(onClick = onShare) { Text("Share") }
|
||||||
TextButton(onClick = onSave) { Text("Save") }
|
TextButton(onClick = onSave) { Text("Save") }
|
||||||
TextButton(onClick = onOpen) { Text("Open") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -162,13 +142,11 @@ fun PreviewPdfGenerationDialogDuringGeneration() {
|
|||||||
MyScanTheme {
|
MyScanTheme {
|
||||||
PdfGenerationDialog(
|
PdfGenerationDialog(
|
||||||
filename = "scan_20250702_174042.pdf",
|
filename = "scan_20250702_174042.pdf",
|
||||||
isGenerating = true,
|
|
||||||
pdf = null,
|
pdf = null,
|
||||||
onFilenameChange = {},
|
onFilenameChange = {},
|
||||||
onDismiss = {},
|
onDismiss = {},
|
||||||
onShare = {},
|
onShare = {},
|
||||||
onSave = {},
|
onSave = {},
|
||||||
onOpen = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,13 +157,11 @@ fun PreviewPdfGenerationDialogAfterGeneration() {
|
|||||||
MyScanTheme {
|
MyScanTheme {
|
||||||
PdfGenerationDialog(
|
PdfGenerationDialog(
|
||||||
filename = "scan_20250702_174042.pdf",
|
filename = "scan_20250702_174042.pdf",
|
||||||
isGenerating = false,
|
|
||||||
pdf = GeneratedPdf("file://fake.pdf".toUri(), 42897L, 3),
|
pdf = GeneratedPdf("file://fake.pdf".toUri(), 42897L, 3),
|
||||||
onFilenameChange = {},
|
onFilenameChange = {},
|
||||||
onDismiss = {},
|
onDismiss = {},
|
||||||
onShare = {},
|
onShare = {},
|
||||||
onSave = {},
|
onSave = {},
|
||||||
onOpen = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user