Save PDF and share it

This commit is contained in:
Pierre-Yves Nicolas
2025-05-31 13:42:24 +02:00
parent 453923b42d
commit f8b9f47782
5 changed files with 86 additions and 2 deletions

View File

@@ -28,6 +28,15 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application> </application>
</manifest> </manifest>

View File

@@ -1,7 +1,11 @@
package org.mydomain.myscan package org.mydomain.myscan
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@@ -17,13 +21,16 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
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.content.FileProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.PagePreviewScreen import org.mydomain.myscan.view.PagePreviewScreen
import org.opencv.android.OpenCVLoader import org.opencv.android.OpenCVLoader
import java.io.File
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -37,6 +44,7 @@ class MainActivity : ComponentActivity() {
val currentScreen by viewModel.currentScreen.collectAsState() val currentScreen by viewModel.currentScreen.collectAsState()
// TODO should uiState own currentScreen? // TODO should uiState own currentScreen?
val uiState by viewModel.uiState.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
MyScanTheme { MyScanTheme {
Scaffold { innerPadding -> Scaffold { innerPadding ->
Column { Column {
@@ -51,7 +59,8 @@ class MainActivity : ComponentActivity() {
PagePreviewScreen ( PagePreviewScreen (
image = screen.image, image = screen.image,
isProcessing = screen.isProcessing, isProcessing = screen.isProcessing,
onBackPressed = { viewModel.navigateTo(Screen.Camera) } onBackPressed = { viewModel.navigateTo(Screen.Camera) },
onSavePressed = createPdfAndShare(context)
) )
} }
} }
@@ -61,6 +70,27 @@ class MainActivity : ComponentActivity() {
} }
} }
private fun createPdfAndShare(context: Context): (Bitmap) -> Unit = { bitmap ->
val outputDir = File(cacheDir, "pdfs").apply { mkdirs() }
val outputFile = File(outputDir, "scan_${System.currentTimeMillis()}.pdf")
val success = createPdfFromBitmaps(listOf(bitmap), outputFile)
if (success) {
val uri = FileProvider.getUriForFile(
context,
"${applicationContext.packageName}.fileprovider",
outputFile
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = "application/pdf"
putExtra(Intent.EXTRA_STREAM, uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(shareIntent, "Share PDF"))
} else {
Toast.makeText(context, "Failed to save PDF", Toast.LENGTH_SHORT).show()
}
}
private fun initOpenCV() { private fun initOpenCV() {
if (!OpenCVLoader.initLocal()) { if (!OpenCVLoader.initLocal()) {
Log.e("OpenCV", "Initialization failed") Log.e("OpenCV", "Initialization failed")

View File

@@ -0,0 +1,29 @@
package org.mydomain.myscan
import android.graphics.Bitmap
import android.graphics.pdf.PdfDocument
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
fun createPdfFromBitmaps(bitmaps: List<Bitmap>, outputFile: File): Boolean {
val document = PdfDocument()
try {
for ((index, bitmap) in bitmaps.withIndex()) {
val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, index + 1).create()
val page = document.startPage(pageInfo)
page.canvas.drawBitmap(bitmap, 0f, 0f, null)
document.finishPage(page)
}
FileOutputStream(outputFile).use { outputStream ->
document.writeTo(outputStream)
}
return true
} catch (e: IOException) {
Log.e("MyScan", "Error writing PDF: ${e.message}")
return false
} finally {
document.close()
}
}

View File

@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -22,7 +23,8 @@ import androidx.compose.ui.unit.dp
fun PagePreviewScreen( fun PagePreviewScreen(
image: Bitmap?, image: Bitmap?,
isProcessing: Boolean, isProcessing: Boolean,
onBackPressed: () -> Unit onBackPressed: () -> Unit,
onSavePressed: (Bitmap) -> Unit,
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
when { when {
@@ -39,6 +41,14 @@ fun PagePreviewScreen(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Fit contentScale = ContentScale.Fit
) )
Button (
onClick = { onSavePressed(image) },
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(16.dp)
) {
Text("Save as PDF")
}
} }
else -> { else -> {
Text( Text(

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path
name="pdfs"
path="pdfs/" />
</paths>