Save PDF and share it
This commit is contained in:
@@ -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>
|
||||||
@@ -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")
|
||||||
|
|||||||
29
app/src/main/java/org/mydomain/myscan/PdfGeneration.kt
Normal file
29
app/src/main/java/org/mydomain/myscan/PdfGeneration.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(
|
||||||
|
|||||||
6
app/src/main/res/xml/file_paths.xml
Normal file
6
app/src/main/res/xml/file_paths.xml
Normal 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>
|
||||||
Reference in New Issue
Block a user