From 86f5b270936489aeefe6a07dbcedcfd355b1460d Mon Sep 17 00:00:00 2001
From: Pierre-Yves Nicolas <6371790+pynicolas@users.noreply.github.com>
Date: Sun, 1 Jun 2025 20:21:34 +0200
Subject: [PATCH] Feature: save PDF to device
---
app/src/main/AndroidManifest.xml | 2 +
.../java/org/mydomain/myscan/MainActivity.kt | 44 +++++++++++++++++--
.../java/org/mydomain/myscan/PdfGeneration.kt | 37 +++++-----------
.../org/mydomain/myscan/view/PagePreview.kt | 15 +++++--
4 files changed, 67 insertions(+), 31 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1ff3736..842d96e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,8 @@
android:name="android.hardware.camera"
android:required="false" />
+
+
Unit = { bitmap ->
val outputDir = File(cacheDir, "pdfs").apply { mkdirs() }
val outputFile = File(outputDir, "scan_${System.currentTimeMillis()}.pdf")
- val success = createPdfFromBitmaps(listOf(bitmap), outputFile)
+ val document = createPdfFromBitmaps(listOf(bitmap))
+ var success = true
+ try {
+ FileOutputStream(outputFile).use { outputStream ->
+ document.writeTo(outputStream)
+ }
+ } catch (_: IOException) {
+ Toast.makeText(context, "Failed to share PDF", Toast.LENGTH_SHORT).show()
+ success = false
+ } finally {
+ document.close()
+ }
if (success) {
val uri = FileProvider.getUriForFile(
context,
@@ -74,8 +90,30 @@ class MainActivity : ComponentActivity() {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(shareIntent, "Share PDF"))
- } else {
+ }
+ }
+
+ fun createPdfAndSave(context: Context): (Bitmap) -> Unit = { bitmap ->
+ val document = createPdfFromBitmaps(listOf(bitmap))
+ try {
+ val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ if (!downloadsDir.exists()) downloadsDir.mkdirs()
+ val file = File(downloadsDir, "scan_${System.currentTimeMillis()}.pdf")
+ val outputStream = FileOutputStream(file)
+ document.writeTo(outputStream)
+ outputStream.flush()
+ outputStream.close()
+
+ MediaScannerConnection.scanFile(
+ context, arrayOf(file.absolutePath), arrayOf("application/pdf"), null
+ )
+
+ Toast.makeText(context, "Saved PDF in Downloads", Toast.LENGTH_SHORT).show()
+ } catch (e: Exception) {
+ Log.e("MyScan", "Failed to save PDF", e)
Toast.makeText(context, "Failed to save PDF", Toast.LENGTH_SHORT).show()
+ } finally {
+ document.close()
}
}
diff --git a/app/src/main/java/org/mydomain/myscan/PdfGeneration.kt b/app/src/main/java/org/mydomain/myscan/PdfGeneration.kt
index c894cd3..1890300 100644
--- a/app/src/main/java/org/mydomain/myscan/PdfGeneration.kt
+++ b/app/src/main/java/org/mydomain/myscan/PdfGeneration.kt
@@ -3,37 +3,24 @@ package org.mydomain.myscan
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.pdf.PdfDocument
-import android.util.Log
import androidx.core.graphics.scale
import java.io.ByteArrayOutputStream
-import java.io.File
-import java.io.FileOutputStream
-import java.io.IOException
import kotlin.math.max
-fun createPdfFromBitmaps(bitmaps: List, outputFile: File): Boolean {
+fun createPdfFromBitmaps (bitmaps: List): PdfDocument {
val document = PdfDocument()
- try {
- for ((index, bitmap) in bitmaps.map { resizeImage(it) }.withIndex()) {
- val jpegStream = ByteArrayOutputStream()
- bitmap.compress(Bitmap.CompressFormat.JPEG, 72, jpegStream)
- val compressedBytes = jpegStream.toByteArray()
- val compressedBitmap = BitmapFactory.decodeByteArray(compressedBytes, 0, compressedBytes.size)
- val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, index + 1).create()
- val page = document.startPage(pageInfo)
- page.canvas.drawBitmap(compressedBitmap, 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()
+ for ((index, bitmap) in bitmaps.map { resizeImage(it) }.withIndex()) {
+ val jpegStream = ByteArrayOutputStream()
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 72, jpegStream)
+ val compressedBytes = jpegStream.toByteArray()
+ val compressedBitmap =
+ BitmapFactory.decodeByteArray(compressedBytes, 0, compressedBytes.size)
+ val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, index + 1).create()
+ val page = document.startPage(pageInfo)
+ page.canvas.drawBitmap(compressedBitmap, 0f, 0f, null)
+ document.finishPage(page)
}
+ return document
}
fun resizeImage(original: Bitmap): Bitmap {
diff --git a/app/src/main/java/org/mydomain/myscan/view/PagePreview.kt b/app/src/main/java/org/mydomain/myscan/view/PagePreview.kt
index 4dc2e4c..b49377b 100644
--- a/app/src/main/java/org/mydomain/myscan/view/PagePreview.kt
+++ b/app/src/main/java/org/mydomain/myscan/view/PagePreview.kt
@@ -3,8 +3,11 @@ package org.mydomain.myscan.view
import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
@@ -25,6 +28,7 @@ fun PagePreviewScreen(
isProcessing: Boolean,
onBackPressed: () -> Unit,
onSavePressed: (Bitmap) -> Unit,
+ onSharePressed: (Bitmap) -> Unit,
) {
Box(modifier = Modifier.fillMaxSize()) {
when {
@@ -41,13 +45,18 @@ fun PagePreviewScreen(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Fit
)
- Button (
- onClick = { onSavePressed(image) },
+ Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(16.dp)
) {
- Text("Save as PDF")
+ Button (onClick = { onSavePressed(image) }) {
+ Text("Save PDF")
+ }
+ Spacer(modifier = Modifier.width(8.dp))
+ Button (onClick = { onSharePressed(image) }) {
+ Text("Share PDF")
+ }
}
}
else -> {