Skip to main content

Ready-to-Use UI v.1.0 -> v.2.0

Introduction

The improved Document Scanner SDK RTU UI v.2.0 further streamlines the integration of our scanning functionalities into your mobile app and provides a tried and true user experience. It also adds several new features – most notably dedicated screens for scanning, reviewing, and editing documents. Thanks to in-depth configuration options, you can tailor the scanning flow to your use case.

If you are currently using RTU UI v.1.0 in your app, this guide will help you migrate to v.2.0.

Key concept differences

Document scanning flow

The new version of the Ready-to-Use UI provides not only separate scanning and cropping screens but also allows you to easily integrate the entire document scanning flow into your application.

The document scanning flow consists of the following screens combined in a single scanning process:

  • Introduction Screen
  • Scanning Screen
  • Acknowledge Screen
  • Review Screen
  • Crop Screen
  • Reorder Screen

You may also disable certain screens if they are unnecessary for your use case.

All screens are highly customizable, allowing you to adapt the UI to your app’s design and branding.

See more details in the Ready-to-Use UI page.

Document API

The new version of the Ready-to-Use UI introduces a new approach to handling scanned documents. Instead of working with Page objects, you now work with Document objects.

Previously, legacy Page objects were stored in the file system as folders with UUIDs as folder names. Each Page folder contained the original image and the document image.

In v.2.0, the Document Scanner Flow produces a Document object instead of a list of Page objects. The Document object contains a list of new Page objects, each with metadata like the cropping polygon, rotation, filters, document detection status, and the result from the Document Quality Analyzer.

You can access the Document object directly from the result of the Document Scanning screen or load it from the SDK’s Document API using the document UUID.

The new Document API provides more convenient methods for managing documents rather than separate pages. You can manage document pages, apply filters, and export the document as a PDF or TIFF file, or delete the document and all the files associated with it.

Migration guide

Update Scanbot SDK version

The Document Scanner SDK RTU UI v.2.0 was introduced in version 6.0.0. To use the new RTU UI, you need to update the Scanbot SDK to version 6.0.0 or higher.

You may find the latest version of the Scanbot SDK in the Changelog.

Add RTU UI v.2.0 dependency

To add the Ready-to-Use UI v.2.0 to your project, you need to add the following dependency to your build.gradle.kts file:

// Ready-to-use UI v2 dependency:
implementation("io.scanbot:rtu-ui-v2-bundle:$latestSdkVersion")

// Required for the Acknowledge screen and the Document Quality Analyser feature:
implementation("io.scanbot:sdk-multitasktext-assets:$latestSdkVersion")

To make the transition smoother, you can keep both dependencies until the migration process is finished. You can remove the old dependency afterward.

// This dependency can be removed as soon as the migration is finished:
implementation("io.scanbot:sdk-package-ui:$scanbotSdkVersion")

DocumentScannerActivity migration

Update the launcher and result processing

The way you launch the Ready-to-Use UI and process the results has changed. The new version of the Ready-to-Use UI is more flexible and allows you to configure the UI more easily.

Let's see how you can update the launcher and result processing for the Ready-to-Use UI v.1.0 and v.2.0.

Imagine we have the MainActivity, where we call the Ready-to-Use UI to scan a document and display the first page preview image.

// ... other imports
import io.scanbot.sdk.ui.registerForActivityResultOk
import io.scanbot.sdk.ui.view.camera.DocumentScannerActivity
import io.scanbot.sdk.ui.view.camera.configuration.DocumentScannerConfiguration

// Your activity class:
class MainActivity : AppCompatActivity() {
private lateinit var documentScannerResultLauncher: ActivityResultLauncher<DocumentScannerConfiguration>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val previewImageView = findViewById<ImageView>(R.id.first_page_image_preview)

documentScannerResultLauncher =
registerForActivityResultOk(
DocumentScannerActivity.ResultContract()
) { resultEntity ->
resultEntity.result?.get(0)?.let {
// In v1 you had to use PageFileStorage to access the image bitmap:
val previewImage =
ScanbotSDK(context = this).createPageFileStorage().getPreviewImage(
it.pageId, type = PageFileType.DOCUMENT
)
previewImageView.setImageBitmap(previewImage)
}
}

findViewById<Button>(R.id.open_document_scanner).setOnClickListener {
openDocumentScannerRtuV1()
}
}
// ...
}

This is how you can update the launcher and result processing for the Ready-to-Use UI v.2.0:

// ... other imports
import io.scanbot.sdk.ui_v2.common.activity.registerForActivityResultOk
import io.scanbot.sdk.ui_v2.document.configuration.DocumentScanningFlow
import io.scanbot.sdk.ui_v2.document.DocumentScannerActivity

// Your activity class:
class MainActivity : AppCompatActivity() {
private lateinit var documentScannerResultLauncher: ActivityResultLauncher<DocumentScanningFlow>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val previewImageView = findViewById<ImageView>(R.id.first_page_image_preview)

documentScannerResultLauncher =
registerForActivityResultOk(DocumentScannerActivity.ResultContract(this)) {
resultEntity: DocumentScannerActivity.Result ->
val result: Document? = resultEntity.result
val pages: List<Page>? = result?.pages
pages?.get(0)?.let {
// in v2 you can access the image bitmap directly from the Page:
val previewImage = it.documentPreviewImage
previewImageView.setImageBitmap(previewImage)
}
}

findViewById<Button>(R.id.open_document_scanner).setOnClickListener {
openDocumentScannerRtuV2()
}
}
// ...
}

Update the Ready-to-Use UI configuration

The configuration of the Ready-to-Use UI has changed. The new version of the Ready-to-Use UI is more flexible and allows you to configure the UI more easily.

Imagine you had the following configuration for the Ready-to-Use UI v.1.0:

// ... other imports
import io.scanbot.sdk.ui.view.camera.configuration.DocumentScannerConfiguration

// ..
// in your Activity class:
private fun openDocumentScannerRtuV1() {
// Customize text resources, behavior and UI:
val configuration = DocumentScannerConfiguration()

configuration.setIgnoreBadAspectRatio(true)
configuration.setAutoSnappingSensitivity(0.75f)

configuration.setTopBarBackgroundColor(Color.BLUE)
configuration.setBottomBarBackgroundColor(Color.BLUE)

configuration.setTextHintOK("Don't move.\nCapturing document...")

configuration.setMultiPageButtonHidden(true)
configuration.setMultiPageEnabled(false)

documentScannerResultLauncher.launch(configuration)
}

The new configuration for the Ready-to-Use UI v.2.0 looks like this:

// ... other imports
import io.scanbot.sdk.ui_v2.common.ScanbotColor
import io.scanbot.sdk.ui_v2.document.configuration.AcknowledgementMode
import io.scanbot.sdk.ui_v2.document.configuration.CaptureFeedback
import io.scanbot.sdk.ui_v2.document.configuration.DocumentScanningFlow
import io.scanbot.sdk.ui_v2.document.configuration.PageSnapFeedbackMode

// ...
// in your Activity class:
private fun openDocumentScannerRtuV2() {
// Customize text resources, behavior and UI:
val configuration = DocumentScanningFlow().apply {
screens.camera.apply {
cameraConfiguration.apply {
// Equivalent to setIgnoreBadAspectRatio(true)
ignoreBadAspectRatio = true

// Equivalent to setAutoSnappingSensitivity(0.75f)
autoSnappingSensitivity = 0.75
}

// Ready-to-Use UI v2 contains an acknowledgment screen to
// verify the captured document with the built-in Document Quality Analyzer.
// You can still disable this step:
acknowledgement.acknowledgementMode = AcknowledgementMode.NONE

// When you disable the acknowledgment screen, you can enable the capture feedback,
// there are different options available, for example you can display a checkmark animation:
captureFeedback = CaptureFeedback(
snapFeedbackMode = PageSnapFeedbackMode.pageSnapCheckMarkAnimation()
)

// You may hide the import button in the camera screen, if you don't need it:
bottomBar.importButton.visible = false
}

// Equivalent to setBottomBarBackgroundColor(Color.RED), but not recommended:
appearance.bottomBarBackgroundColor = ScanbotColor(Color.BLUE)

// However, now all the colors can be conveniently set using the Palette object:
palette.apply {
sbColorPrimary = ScanbotColor(Color.BLUE)
sbColorOnPrimary = ScanbotColor(Color.WHITE)
sbColorOnSurfaceVariant = ScanbotColor(Color.DKGRAY)
// ..
}

// Now all the text resources are in the localization object
localization.apply {
// Equivalent to setTextHintOK("Don't move.\nCapturing document...")
cameraUserGuidanceReadyToCapture = "Don't move.\nCapturing document..."
}

// Ready-to-Use UI v2 contains a review screen, you can disable it:
screens.review.enabled = false

// Multi Page button is always hidden in RTU v2
// Therefore setMultiPageButtonHidden(true) is not available

// Equivalent to setMultiPageEnabled(false)
outputSettings.pagesScanLimit = 1
}

CroppingActivity migration

As well as the DocumentScannerActivity, the CroppingActivity has been updated to version v.2.0. Even though you may use the review screen to call the cropping screen, you can also call the cropping screen separately.

Update the launcher and result processing

Same as for the DocumentScannerActivity, the way you launch the CroppingActivity and process the results has changed.

// ... other imports
import io.scanbot.sdk.ui.registerForActivityResultOk
import io.scanbot.sdk.ui.view.edit.CroppingActivity
import io.scanbot.sdk.ui.view.edit.configuration.CroppingConfiguration

// Your activity class:
class MainActivity : AppCompatActivity() {
private lateinit var croppingResultLauncher: ActivityResultLauncher<CroppingConfiguration>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val previewImageView = findViewById<ImageView>(R.id.first_page_image_preview)

croppingResultLauncher =
registerForActivityResultOk(
CroppingActivity.ResultContract()
) { resultEntity ->
resultEntity.result?.let {
// In v1 you had to use PageFileStorage to access the image bitmap:
val previewImage =
ScanbotSDK(context = this).createPageFileStorage().getPreviewImage(
it.pageId, type = PageFileType.DOCUMENT
)
previewImageView.setImageBitmap(previewImage)
}
}

findViewById<Button>(R.id.open_cropping_ui).setOnClickListener {
openCroppingRtuV1()
}
}
// ...
}

This is how you can update the launcher and result processing for the Ready-to-Use UI v.2.0:

// ... other imports
import io.scanbot.sdk.ui_v2.common.activity.registerForActivityResultOk
import io.scanbot.sdk.ui_v2.document.configuration.DocumentScanningFlow
import io.scanbot.sdk.ui_v2.document.DocumentScannerActivity

// Your activity class:
class MainActivity : AppCompatActivity() {
private lateinit var croppingResultLauncher: ActivityResultLauncher<CroppingConfiguration>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val previewImageView = findViewById<ImageView>(R.id.first_page_image_preview)

croppingResultLauncher =
registerForActivityResultOk(
CroppingActivity.ResultContract()
) { resultEntity ->
resultEntity.result?.let {
val documentApi = ScanbotSDK(this).documentApi
// This way you can access the document from the Document API:
val document = documentApi.loadDocument(documentId = it.documentUuid)
val previewImage = document?.pageWithId(it.pageUuid)?.documentPreviewImage
previewImageView.setImageBitmap(previewImage)
}
}

findViewById<Button>(R.id.open_document_scanner).setOnClickListener {
openCroppingRtuV2()
}
}
// ...
}

Update the Ready-to-Use UI configuration

The configuration of the Ready-to-Use UI has changed. The new version of the Ready-to-Use UI is more flexible and allows you to configure the UI more easily.

Imagine you had the following configuration for the Ready-to-Use UI v.1.0:

// ... other imports
import io.scanbot.sdk.persistence.page.legacy.Page
import io.scanbot.sdk.ui.view.edit.configuration.CroppingConfiguration

// ..
// in your Activity class:
private fun openCroppingRtuV1() {
// Customize text resources, behavior and UI:
val configuration = CroppingConfiguration(
// In v1, you passed the Page object you needed to re-crop:
Page(pageId)
)

configuration.setDoneButtonTitle("Apply")

configuration.setTopBarBackgroundColor(Color.BLUE)
configuration.setBottomBarBackgroundColor(Color.BLUE)

croppingResultLauncher.launch(configuration)
}

The new configuration for the Ready-to-Use UI v.2.0 looks like this:

// ... other imports
import io.scanbot.sdk.ui_v2.common.ScanbotColor
import io.scanbot.sdk.ui_v2.document.configuration.CroppingConfiguration

// ...
// in your Activity class:
private fun openCroppingRtuV2() {
// Customize text resources, behavior and UI:
val configuration = CroppingConfiguration(
// Now you need to pass the document UUID and the page UUID:
documentUuid = documentUuid,
pageUuid = pageUuid,
)

// Now you can apply your business colors using the Palette object:
configuration.palette.apply {
sbColorPrimary = ScanbotColor(Color.BLUE)
// ..
}

croppingResultLauncher.launch(configuration)
}

FinderDocumentScannerActivity migration

Unlike in the Ready-to-Use UI v.1.0 where you could use a separate FinderDocumentScannerActivity, in the new version of the Ready-to-Use UI, you can use the DocumentScanningFlow to configure the camera screen with the finder aspect ratio.

Update the launcher and result processing

With Ready-to-Use UI v.1.0 you could use the following code to launch the FinderDocumentScannerActivity:

// ... other imports
import io.scanbot.sdk.ui.registerForActivityResultOk
import io.scanbot.sdk.ui.view.camera.FinderDocumentScannerActivity
import io.scanbot.sdk.ui.view.camera.configuration.FinderDocumentScannerConfiguration

// Your activity class:
class MainActivity : AppCompatActivity() {
private lateinit var finderScannerResultLauncher: ActivityResultLauncher<FinderDocumentScannerConfiguration>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val previewImageView = findViewById<ImageView>(R.id.first_page_image_preview)

finderScannerResultLauncher =
registerForActivityResultOk(
FinderDocumentScannerActivity.ResultContract()
) { resultEntity ->
resultEntity.result?.let {
// In v1 you had to use PageFileStorage to access the image bitmap:
val previewImage =
ScanbotSDK(context = this).createPageFileStorage().getPreviewImage(
it.pageId, type = PageFileType.DOCUMENT
)
previewImageView.setImageBitmap(previewImage)
}
}

findViewById<Button>(R.id.open_document_scanner).setOnClickListener {
openFinderRtuV1()
}
// ...
}

You may achieve similar behavior now using the same DocumentScanningFlow configuration as for the DocumentScannerActivity.

// ... other imports
import io.scanbot.sdk.ui_v2.common.activity.registerForActivityResultOk
import io.scanbot.sdk.ui_v2.document.configuration.DocumentScanningFlow
import io.scanbot.sdk.ui_v2.document.DocumentScannerActivity

// Your activity class:
class MainActivity : AppCompatActivity() {
private lateinit var documentScannerResultLauncher: ActivityResultLauncher<CroppingConfiguration>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val previewImageView = findViewById<ImageView>(R.id.first_page_image_preview)

documentScannerResultLauncher =
registerForActivityResultOk(DocumentScannerActivity.ResultContract(this)) { resultEntity: DocumentScannerActivity.Result ->
resultEntity.result?.pages?.get(0)?.let {
// in v2 you can access the image bitmap directly from the result entity:
val previewImage = it.documentPreviewImage
previewImageView.setImageBitmap(previewImage)
}
}

findViewById<Button>(R.id.open_document_scanner).setOnClickListener {
openDocumentScannerRtuV2withFinder()
}
}
// ...
}

Update the Ready-to-Use UI configuration

The configuration migration would be then similar to the DocumentScannerActivity configuration.

Imagine you had the following configuration for the Ready-to-Use UI v.1.0 for the FinderDocumentScannerActivity:

// ... other imports
import io.scanbot.sdk.ui.view.camera.configuration.FinderDocumentScannerConfiguration

// ..
// in your Activity class:
private fun openFinderRtuV1() {
// Customize text resources, behavior and UI:
val configuration = FinderDocumentScannerConfiguration()

configuration.setTopBarBackgroundColor(Color.BLUE)
configuration.setFinderAspectRatio(AspectRatio(3.0, 4.0))
configuration.setShutterButtonHidden(false)

finderScannerResultLauncher.launch(configuration)
}

The configuration for the Ready-to-Use UI v.2.0 would look like this:

// ... other imports
import io.scanbot.sdk.ui_v2.common.AspectRatio
import io.scanbot.sdk.ui_v2.common.ScanbotColor
import io.scanbot.sdk.ui_v2.document.configuration.AcknowledgementMode
import io.scanbot.sdk.ui_v2.document.configuration.DocumentScanningFlow
import io.scanbot.sdk.ui_v2.document.configuration.PageSnapFeedbackMode
import io.scanbot.sdk.ui_v2.document.configuration.PreviewButton

// ...
// in your Activity class:
private fun openDocumentScannerRtuV2withFinder() {
// Customize text resources, behavior and UI:
val configuration = DocumentScanningFlow().apply {
palette.apply {
sbColorPrimary = ScanbotColor(Color.BLUE)
}
screens.camera.apply {
viewFinder.apply {
visible = true
aspectRatio = AspectRatio(3.0, 4.0)
}
bottomBar.apply {
previewButton = PreviewButton.noButtonMode()
autoSnappingModeButton.visible = false
importButton.visible = false
}
acknowledgement.acknowledgementMode = AcknowledgementMode.NONE
captureFeedback.snapFeedbackMode = PageSnapFeedbackMode.pageSnapCheckMarkAnimation()
}
screens.review.enabled = false
outputSettings.pagesScanLimit = 1
}

documentScannerResultLauncher.launch(configuration)
}

Storage migration

To migrate the stored pages created with the RTU UI v.1.0 and based on the legacy Page objects to the new RTU UI v.2.0 model, you can use the provided migration function.

This function takes the original pages and converts them into a new document. This process takes the old metadata and original images and stores them in a new document, reapplying all the changes.

Take a look at the example below:

// Take a list of legacy pages that represent one document and convert them to a new document.
val legacyPages: List<io.scanbot.sdk.persistence.page.legacy.Page> = ...
val document = legacyPages.toDocument(ScanbotSDK(context), documentImageSizeLimit = 2048)

// Now you may delete `legacyPages` to free up memory

Want to scan longer than one minute?

Generate a free trial license to test the Scanbot SDK thoroughly.

Get your free Trial License

What do you think of this documentation?