diff --git a/classic-components-example/pdf-generation/build.gradle b/classic-components-example/pdf-generation/build.gradle index baf5c167..763eab52 100644 --- a/classic-components-example/pdf-generation/build.gradle +++ b/classic-components-example/pdf-generation/build.gradle @@ -23,10 +23,6 @@ android { } } - kotlin { - jvmToolchain(project.ext.jvmToolchainVersion) - } - packagingOptions { exclude 'META-INF/LICENSE.txt' exclude 'META-INF/LICENSE' @@ -35,13 +31,15 @@ android { exclude 'META-INF/DEPENDENCIES' } - buildFeatures { - viewBinding = true - } + buildFeatures.viewBinding = true } +kotlin.jvmToolchain(project.ext.jvmToolchainVersion) + dependencies { implementation(project(":common")) implementation("androidx.appcompat:appcompat:${project.ext.androidxAppcompatVersion}") - implementation("io.scanbot:sdk-package-1:${project.ext.scanbotSdkVersion}") + + // NOTE: use `sdk-package-1` when OCR feature is not needed: + implementation("io.scanbot:sdk-package-2:${project.ext.scanbotSdkVersion}") } diff --git a/classic-components-example/pdf-generation/src/main/AndroidManifest.xml b/classic-components-example/pdf-generation/src/main/AndroidManifest.xml index 6d6cf0e3..5d06a1e7 100755 --- a/classic-components-example/pdf-generation/src/main/AndroidManifest.xml +++ b/classic-components-example/pdf-generation/src/main/AndroidManifest.xml @@ -16,21 +16,22 @@ android:exported="true"> - + + + + - - diff --git a/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/MainActivity.kt b/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/MainActivity.kt index 6c228c9d..5da20194 100755 --- a/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/MainActivity.kt +++ b/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/MainActivity.kt @@ -1,145 +1,28 @@ package io.scanbot.example import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri import android.os.Bundle -import android.util.Log -import android.view.View -import androidx.activity.result.PickVisualMediaRequest -import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.FileProvider -import androidx.core.net.toFile -import androidx.lifecycle.lifecycleScope -import io.scanbot.common.mapSuccess -import io.scanbot.common.onSuccess -import io.scanbot.example.common.Const import io.scanbot.example.common.applyEdgeToEdge -import io.scanbot.example.common.showToast import io.scanbot.example.databinding.ActivityMainBinding -import io.scanbot.sdk.ScanbotSDK -import io.scanbot.sdk.image.ImageRef -import io.scanbot.sdk.imageprocessing.ParametricFilter -import io.scanbot.sdk.ocr.OcrEngineManager -import io.scanbot.sdk.pdfgeneration.PageSize -import io.scanbot.sdk.pdfgeneration.PdfConfiguration -import io.scanbot.sdk.pdfgeneration.PdfGenerator -import io.scanbot.sdk.util.PolygonHelper -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import java.io.File - -/** -Ths example uses new sdk APIs presented in Scanbot SDK v.8.x.x -Please, check the official documentation for more details: -Result API https://docs.scanbot.io/android/document-scanner-sdk/detailed-setup-guide/result-api/ -ImageRef API https://docs.scanbot.io/android/document-scanner-sdk/detailed-setup-guide/image-ref-api/ - */ class MainActivity : AppCompatActivity() { - private val runOcr = false - private val scanbotSdk by lazy { ScanbotSDK(this) } - private val pdfGenerator by lazy { scanbotSdk.createPdfGenerator(if (runOcr) OcrEngineManager.OcrConfig() else null) } - - private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) } - - private val selectGalleryImageResultLauncher = - // limit to 5 images for example purposes - registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia(5)) { uris -> - if (uris.isEmpty()) { - this@MainActivity.showToast("No images were selected!") - Log.w(Const.LOG_TAG, "No images were selected!") - return@registerForActivityResult - } - - if (!scanbotSdk.licenseInfo.isValid) { - this@MainActivity.showToast("Scanbot SDK license (1-minute trial) has expired!") - Log.w(Const.LOG_TAG, "Scanbot SDK license (1-minute trial) has expired!") - return@registerForActivityResult - } - - lifecycleScope.launch { processDocument(uris, isGrayscaleChecked) } - } - - private val isGrayscaleChecked: Boolean - get() = binding.grayscaleCheckBox.isChecked override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) supportActionBar!!.hide() - applyEdgeToEdge(findViewById(R.id.root_view)) - - binding.scanButton.setOnClickListener { - selectGalleryImageResultLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) - } - } - - private suspend fun processDocument(uris: List, applyGrayscale: Boolean) { - val filters = - if (applyGrayscale) listOf(ParametricFilter.grayscaleFilter()) else emptyList() - withContext(Dispatchers.Main) { binding.progressBar.visibility = View.VISIBLE } - - withContext(Dispatchers.Default) { - scanbotSdk.createDocumentScanner().mapSuccess { documentScanner -> - //can be handled with .getOrNull() if needed - val document = scanbotSdk.documentApi.createDocument() - .getOrReturn() //can be handled with .getOrNull() if needed - uris.asSequence().forEach { uri -> - val imageRef = contentResolver.openInputStream(uri)?.use { inputStream -> - ImageRef.fromInputStream(inputStream) - } - if (imageRef == null) { - Log.w(Const.LOG_TAG, "Cannot open input stream from URI: $uri") - return@forEach - } - val newPolygon = documentScanner.run(imageRef).getOrNull()?.pointsNormalized - ?: PolygonHelper.getFullPolygon() - val page = document.addPage(imageRef) - .getOrReturn() //can be handled with .getOrNull() if needed - page.apply(newPolygon = newPolygon, newFilters = filters) - } - pdfGenerator.generate( - document, - PdfConfiguration.default().copy(pageSize = PageSize.A4) - ) - document.pdfUri.toFile() - }.onSuccess { renderedPdfFile -> - runBlocking(Dispatchers.Main) { - binding.progressBar.visibility = View.GONE - openPdfDocument(renderedPdfFile) - } - } - } - } + applyEdgeToEdge(binding.root) - private fun openPdfDocument(file: File) { - val uri = FileProvider.getUriForFile(this, "${this.packageName}.provider", file) - - val openIntent = Intent(Intent.ACTION_VIEW).apply { - setDataAndType(uri, "application/pdf") - flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + binding.pdfButton.setOnClickListener { + val intent = Intent(this, PdfActivity::class.java) + startActivity(intent) } - if (intent.resolveActivity(packageManager) != null) { - val chooser = Intent.createChooser(openIntent, file.name) - val resInfoList = this.packageManager.queryIntentActivities( - chooser, - PackageManager.MATCH_DEFAULT_ONLY - ) - - for (resolveInfo in resInfoList) { - val packageName = resolveInfo.activityInfo.packageName - grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) - } - startActivity(chooser) - } else { - // Handle the case where no app can open PDF files - showToast("No app found to open PDF files") - Log.w(Const.LOG_TAG, "No app found to open PDF files") + binding.pdfWithOcrButton.setOnClickListener { + val intent = Intent(this, PdfWithOcrActivity::class.java) + startActivity(intent) } } } diff --git a/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/PdfActivity.kt b/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/PdfActivity.kt new file mode 100644 index 00000000..e78c8b04 --- /dev/null +++ b/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/PdfActivity.kt @@ -0,0 +1,148 @@ +package io.scanbot.example + +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.FileProvider +import androidx.core.net.toFile +import androidx.lifecycle.lifecycleScope +import io.scanbot.common.mapSuccess +import io.scanbot.common.onFailure +import io.scanbot.common.onSuccess +import io.scanbot.example.common.Const +import io.scanbot.example.common.applyEdgeToEdge +import io.scanbot.example.common.showToast +import io.scanbot.example.databinding.ActivityPdfBinding +import io.scanbot.sdk.ScanbotSDK +import io.scanbot.sdk.image.ImageRef +import io.scanbot.sdk.imageprocessing.ParametricFilter +import io.scanbot.sdk.pdfgeneration.PageSize +import io.scanbot.sdk.pdfgeneration.PdfConfiguration +import io.scanbot.sdk.util.PolygonHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import java.io.File + +/** This example uses new sdk APIs presented in Scanbot SDK v.8.x.x + * + * Please, check the official documentation for more details: + * Result API https://docs.scanbot.io/android/document-scanner-sdk/detailed-setup-guide/result-api/ + * ImageRef API https://docs.scanbot.io/android/document-scanner-sdk/detailed-setup-guide/image-ref-api/ + */ +class PdfActivity : AppCompatActivity() { + + private val scanbotSdk by lazy { ScanbotSDK(this) } + + private val binding by lazy { ActivityPdfBinding.inflate(layoutInflater) } + + private val isGrayscaleChecked: Boolean + get() = binding.grayscaleCheckBox.isChecked + + private val selectGalleryImageResultLauncher = + // limit to 5 images for example purposes + registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia(5)) { uris -> + if (uris.isEmpty()) { + this@PdfActivity.showToast("No images were selected!") + Log.w(Const.LOG_TAG, "No images were selected!") + return@registerForActivityResult + } + + if (!scanbotSdk.licenseInfo.isValid) { + this@PdfActivity.showToast("Scanbot SDK license (1-minute trial) has expired!") + Log.w(Const.LOG_TAG, "Scanbot SDK license (1-minute trial) has expired!") + return@registerForActivityResult + } + + lifecycleScope.launch { processDocument(uris, isGrayscaleChecked) } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + supportActionBar!!.hide() + applyEdgeToEdge(findViewById(R.id.root_view)) + + binding.scanButton.setOnClickListener { + selectGalleryImageResultLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + } + } + + private suspend fun processDocument(uris: List, applyGrayscale: Boolean) { + val filters = + if (applyGrayscale) listOf(ParametricFilter.grayscaleFilter()) else emptyList() + withContext(Dispatchers.Main) { binding.progressBar.visibility = View.VISIBLE } + + withContext(Dispatchers.Default) { + scanbotSdk.createDocumentScanner().mapSuccess { documentScanner -> + + val document = scanbotSdk.documentApi.createDocument() + .getOrReturn() // can be handled with .getOrNull() if needed + + uris.asSequence().forEach { uri -> + val imageRef = contentResolver.openInputStream(uri)?.use { inputStream -> + ImageRef.fromInputStream(inputStream) + } + if (imageRef == null) { + Log.w(Const.LOG_TAG, "Cannot open input stream from URI: $uri") + return@forEach + } + val newPolygon = documentScanner.run(imageRef).getOrNull()?.pointsNormalized + ?: PolygonHelper.getFullPolygon() + val page = document.addPage(imageRef).getOrReturn() // can be handled with .getOrNull() if needed + page.apply(newPolygon = newPolygon, newFilters = filters) + } + + val pdfOcrGenerator = scanbotSdk.createPdfGenerator() + val pdfConfig = PdfConfiguration.default().copy(pageSize = PageSize.A4) + + pdfOcrGenerator.generate(document, pdfConfig).getOrReturn() // can be handled with .getOrNull() if needed + + document.pdfUri.toFile() + }.onSuccess { renderedPdfFile -> + runBlocking(Dispatchers.Main) { + binding.progressBar.visibility = View.GONE + openPdfDocument(renderedPdfFile) + } + }.onFailure { error -> + runBlocking(Dispatchers.Main) { + binding.progressBar.visibility = View.GONE + Log.w(Const.LOG_TAG, "Error during PDF generation: ${error.message}", error) + showToast("Error during PDF generation:\n${error.message}") + } + } + } + } + + private fun openPdfDocument(file: File) { + val uri = FileProvider.getUriForFile(this, "${this.packageName}.provider", file) + + val openIntent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(uri, "application/pdf") + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + } + + if (intent.resolveActivity(packageManager) != null) { + val chooser = Intent.createChooser(openIntent, file.name) + val resInfoList = packageManager.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY) + + for (resolveInfo in resInfoList) { + val packageName = resolveInfo.activityInfo.packageName + grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + startActivity(chooser) + } else { + // Handle the case where no app can open PDF files + showToast("No app found to open PDF files") + Log.w(Const.LOG_TAG, "No app found to open PDF files") + } + } +} diff --git a/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/PdfWithOcrActivity.kt b/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/PdfWithOcrActivity.kt new file mode 100644 index 00000000..eb59f928 --- /dev/null +++ b/classic-components-example/pdf-generation/src/main/java/io/scanbot/example/PdfWithOcrActivity.kt @@ -0,0 +1,149 @@ +package io.scanbot.example + +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.FileProvider +import androidx.core.net.toFile +import androidx.lifecycle.lifecycleScope +import io.scanbot.common.mapSuccess +import io.scanbot.common.onFailure +import io.scanbot.common.onSuccess +import io.scanbot.example.common.Const +import io.scanbot.example.common.applyEdgeToEdge +import io.scanbot.example.common.showToast +import io.scanbot.example.databinding.ActivityPdfBinding +import io.scanbot.sdk.ScanbotSDK +import io.scanbot.sdk.image.ImageRef +import io.scanbot.sdk.imageprocessing.ParametricFilter +import io.scanbot.sdk.ocr.OcrEngineManager +import io.scanbot.sdk.pdfgeneration.PageSize +import io.scanbot.sdk.pdfgeneration.PdfConfiguration +import io.scanbot.sdk.util.PolygonHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import java.io.File + +/** This example uses new sdk APIs presented in Scanbot SDK v.8.x.x + * + * Please, check the official documentation for more details: + * Result API https://docs.scanbot.io/android/document-scanner-sdk/detailed-setup-guide/result-api/ + * ImageRef API https://docs.scanbot.io/android/document-scanner-sdk/detailed-setup-guide/image-ref-api/ + */ +class PdfWithOcrActivity : AppCompatActivity() { + + private val scanbotSdk by lazy { ScanbotSDK(this) } + + private val binding by lazy { ActivityPdfBinding.inflate(layoutInflater) } + + private val isGrayscaleChecked: Boolean + get() = binding.grayscaleCheckBox.isChecked + + private val selectGalleryImageResultLauncher = + // limit to 5 images for example purposes + registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia(5)) { uris -> + if (uris.isEmpty()) { + this@PdfWithOcrActivity.showToast("No images were selected!") + Log.w(Const.LOG_TAG, "No images were selected!") + return@registerForActivityResult + } + + if (!scanbotSdk.licenseInfo.isValid) { + this@PdfWithOcrActivity.showToast("Scanbot SDK license (1-minute trial) has expired!") + Log.w(Const.LOG_TAG, "Scanbot SDK license (1-minute trial) has expired!") + return@registerForActivityResult + } + + lifecycleScope.launch { processDocument(uris, isGrayscaleChecked) } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + supportActionBar!!.hide() + applyEdgeToEdge(binding.root) + + binding.scanButton.setOnClickListener { + selectGalleryImageResultLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + } + } + + private suspend fun processDocument(uris: List, applyGrayscale: Boolean) { + val filters = + if (applyGrayscale) listOf(ParametricFilter.grayscaleFilter()) else emptyList() + withContext(Dispatchers.Main) { binding.progressBar.visibility = View.VISIBLE } + + withContext(Dispatchers.Default) { + scanbotSdk.createDocumentScanner().mapSuccess { documentScanner -> + + val document = scanbotSdk.documentApi.createDocument() + .getOrReturn() // can be handled with .getOrNull() if needed + + uris.asSequence().forEach { uri -> + val imageRef = contentResolver.openInputStream(uri)?.use { inputStream -> + ImageRef.fromInputStream(inputStream) + } + if (imageRef == null) { + Log.w(Const.LOG_TAG, "Cannot open input stream from URI: $uri") + return@forEach + } + val newPolygon = documentScanner.run(imageRef).getOrNull()?.pointsNormalized + ?: PolygonHelper.getFullPolygon() + val page = document.addPage(imageRef).getOrReturn() //can be handled with .getOrNull() if needed + page.apply(newPolygon = newPolygon, newFilters = filters) + } + + val pdfOcrGenerator = scanbotSdk.createPdfGenerator(OcrEngineManager.OcrConfig()) + val pdfConfig = PdfConfiguration.default().copy(pageSize = PageSize.A4) + + pdfOcrGenerator.generate(document, pdfConfig).getOrReturn() + + document.pdfUri.toFile() + }.onSuccess { renderedPdfFile -> + runBlocking(Dispatchers.Main) { + binding.progressBar.visibility = View.GONE + openPdfDocument(renderedPdfFile) + } + }.onFailure { error -> + runBlocking(Dispatchers.Main) { + binding.progressBar.visibility = View.GONE + Log.w(Const.LOG_TAG, "Error during PDF generation: ${error.message}", error) + showToast("Error during PDF generation:\n${error.message}") + } + } + } + } + + private fun openPdfDocument(file: File) { + val uri = FileProvider.getUriForFile(this, "${this.packageName}.provider", file) + + val openIntent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(uri, "application/pdf") + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + } + + if (intent.resolveActivity(packageManager) != null) { + val chooser = Intent.createChooser(openIntent, file.name) + val resInfoList = packageManager.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY) + + for (resolveInfo in resInfoList) { + val packageName = resolveInfo.activityInfo.packageName + grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + startActivity(chooser) + } else { + // Handle the case where no app can open PDF files + showToast("No app found to open PDF files") + Log.w(Const.LOG_TAG, "No app found to open PDF files") + } + } +} diff --git a/classic-components-example/pdf-generation/src/main/res/layout/activity_main.xml b/classic-components-example/pdf-generation/src/main/res/layout/activity_main.xml index fef7ccd1..36462b41 100755 --- a/classic-components-example/pdf-generation/src/main/res/layout/activity_main.xml +++ b/classic-components-example/pdf-generation/src/main/res/layout/activity_main.xml @@ -1,44 +1,36 @@ - - -