Android Document Scanner Tutorial – How to integrate our scanning functionalities

This short tutorial will show you how you can integrate the Scanbot SDK into an Android app using Android Studio or similar IDEs. Whether you are still learning Android app development or you already have basic knowledge and this is your first app, this is a great place to start!

Ivan February 10, 2024 12 mins read
app store

Getting started

Requirements:
  • Latest Android Studio version with Android SDK installed
  • Android device running Android 5.0 or later

Android Studio is the most commonly used IDE for developing Android applications. Alternatively, you could use IntelliJ IDEA. In this tutorial, we will only use standard Android SDK features – and the Scanbot SDK.

Create a “Hello World” Android app

In Android Studio, create a blank project. To do that, go to File ➜ New ➜ New Project. Then, select the Empty Activity project template – we won’t need any complex UI or special features. 

On the next screen, choose io.example.myapplication as the package name, then press Finish.

Android Studio will now generate a simple Android project for you.

Note: Make sure that the option Use legacy android.support.libraries is unchecked and that Android Studio generates the Android project using the new AndroidX libraries. 

Currently, the lowest version of the Android SDK supported by our Scanbot SDK is 5.0 (API Level 21). Select a larger value if you want to restrict users with outdated OS versions from installing your app.

Test run

First, connect a mobile device via USB (ADB debugging needs to be enabled). Alternatively, you can use Android Studio’s built-in Android emulator. However, a physical device is recommended to make use of its camera hardware.

Select your device or emulator in the navigation bar and press the Run button. 

You should now see an empty app with a simple “Hello World!” message running on your device.

Add the Scanbot SDK dependencies

Next, we will add the Scanbot Scanner SDK for Android dependency, which comes as a set of Gradle modules. Gradle dependencies let you quickly integrate and update third-party libraries that simplify access to native system APIs like camera, network, or battery status, as well as third-party platform SDKs like Firebase.

For more details about Gradle dependencies, refer to the Android documentation’s “Build dependencies.”

Now, let’s add the Scanbot SDK modules to our project. 

Gradle allows you to pull external dependencies from different repositories. The Scanbot SDK modules are stored on our Nexus repository, and so we need to add it to the list of repositories in the project’s settings.gradle file.

Add the Scanbot repository under the mavenCentral and google repositories:

dependencyResolutionManagement {
   repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
   repositories {
       google()
       mavenCentral()
       maven { url 'https://nexus.scanbot.io/nexus/content/repositories/releases/' }
}

We also need to add the Scanbot SDK dependency to the app module of the application. Depending on the feature set we want, different Gradle dependencies are required. 

In this tutorial, we will only use the Document Scanning feature, which means adding the dependency module io.scanbot:sdk-package-1

For this, open the build.gradle file of the app module.

‍Add the following line to the file: 

implementation "io.scanbot:sdk-package-1:2.2.0"

1.87.1 is the version of the Scanbot SDK for Android we use in this tutorial. You can find the latest version in our changelog.

The Scanbot SDK also provides Ready-to-Use UI (RTU UI) components – Activities that make integrating the scanning features very easy. To use those RTU UI components, also add the following Gradle dependency:

implementation "io.scanbot:sdk-package-ui:2.2.0"

Don’t forget to sync your project every time you add dependencies!

To learn more about other features of the SDK and how to set up each component, please refer to our documentation.

Camera permission and further adjustments

In the app/src/main/AndroidManifest.xml file, add the CAMERA permission and declare that the app uses this feature by adding the following:

<manifest ...>
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-feature android:name="android.hardware.camera"/>
  ...
</manifest>

Since our application will work with high-resolution images, we strongly recommend adding the property android:largeHeap=”true” to the <application> element of the AndroidManifest.xml file, especially for Android versions lower than 7.x:

<application
  android:largeHeap="true"
  ...>

Processing high-resolution images is a memory-intensive task, and this property will ensure your app is allocated enough heap to avoid OutOfMemoryError exceptions.

Scanbot SDK:
Unlimited scanning at a fixed price

Your reliable data capture solution for mobile and web app integration.


Supports all common platforms and frameworks.

It’s coding time

Now, let’s write some Kotlin code to put the SDK features to use. 

Initialize the Scanbot SDK

Scanbot SDK needs to be initialized to work properly. For that, we will use the class ScanbotSDKInitializer. The simplest way of doing that is to add it to our Application class, which is the main entry point of an Android application. 

Let’s add a new class ExampleApplication (NewKotlin class) and implement it as follows:

package io.example.myapplication

import android.app.Application
import io.scanbot.sdk.ScanbotSDKInitializer

class ExampleApplication : Application() {
   override fun onCreate() {
       super.onCreate()

       ScanbotSDKInitializer()
           .license(this, LICENSE_KEY) // Please add a valid trial license key here. See the notes below!
           .initialize(this)
   }
}

Now we declare this class in our AndroidManifest.xml:

<application
   android:largeHeap="true"
    android:name=".ExampleApplication"
   ..>

Done! Now, the Scanbot SDK will be initialized immediately after the user opens the app. 

Next up: Adding a UI that lets us actually scan something with our new app.

Integrate the Document Scanner UI

As mentioned above, the Scanbot Scanner SDK for Android provides ready-to-use UI components for document scanning. We can easily integrate the screen components with just a few lines of code. We will use the class DocumentScannerActivity and define our desired configuration with the DocumentScannerConfiguration class.

First, let’s wrap the SDK Activity call into a simple function named scanDocument() – we will bind it to a button later – and add it to our MainActivity class:

class MainActivity : AppCompatActivity() {
   ...

   private fun startDocument() {
       val configuration = DocumentScannerConfiguration()
       configuration.setTopBarBackgroundColor(Color.RED)
       // It is possible to customize the behavior and appearance of the SDK screen
       val docIntent = DocumentScannerActivity.newIntent(this, configuration)
       startActivityForResult(docIntent, DOCUMENT_SCANNER_ACTIVITY_REQUEST_CODE)
   }

   companion object {
       const val DOCUMENT_SCANNER_ACTIVITY_REQUEST_CODE = 100
   }
}

With DocumentScannerConfiguration, you can pass config parameters to customize colors, text resources, and the behavior of some Document Scanner features. See our API docs on GitHub for more details.

Now let’s add a button to the layout of our MainActivity. For simplicity’s sake, we will also replace the ConstraintLayout with LinearLayout

Change src/main/res/layout/activity_main.xml to look like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/start_scanner_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center"
       android:text="Start Scanner"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

</LinearLayout>

Next, we will bind our scanDocument() method as an onClickListener event to the button we just created in the onCreate method of our MainActivity class:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        findViewById<Button>(R.id.start_scanner_button).setOnClickListener { 
            startDocument()
        }
    }

Let’s run the app and see how the scanning UI looks when scanning a document. 

During the first run, the application will ask for permission to access the device’s camera, as it needs it to perform a scan. Our RTU UI components handle camera permissions automatically, so you don’t need to worry about it.

The Document Scanner screen now appears and the software detects the contours of the document. We can also see that the toolbar color is now red, which we set in the configuration. 

Note: To keep things simple, we didn’t enable the multipage feature of the Document Scanner (see the config parameter setMultiPageEnabled, which is false by default). This means that the scanner UI will automatically close after a single scan.

Next, we will pull the first Page element from the result array to display the cropped document image.

Let’s proceed!

Process and display the scanned image

After the scan, the Document Scanner Activity returns an array of Page objects. These contain all the information about the scanned document. 

As usual in Android development, we need to override the onActivityResult method in the calling Activity to access its results.

First, we make sure that the call is made from our DocumentScannerActivity by checking requestCode and resultCode. Then, to access the pages themselves, we need to use the SNAPPED_PAGE_EXTRA key. In this example, like we said earlier, we are only interested in the first element of the array. We will process the page in the processPagePreview method later.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == DOCUMENT_SCANNER_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
            val snappedPagesExtra = data?.getParcelableArrayExtra(DocumentScannerActivity.SNAPPED_PAGE_EXTRA)
            snappedPagesExtra?.get(0)?.let { page ->
                processPagePreview(page as Page)
            }
        }
    }

    private fun processPagePreview(page: Page) {
        // We will implement it later
    }
   

To access the image contained in the Page object, we need to use the PageFileStorage class. An instance of it can be created by calling the createPageFileStorage() in the ScanbotSDK class. The most convenient place to do this is our onCreate method and saving it as a private property pageFileStorage.

class MainActivity : AppCompatActivity() {
    private lateinit var pageFileStorage: PageFileStorage

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        pageFileStorage = ScanbotSDK(this).createPageFileStorage()
    }

To show a preview, we will add an ImageView widget to our layout. The complete layout of our MainActivity now looks like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/start_scanner_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center"
       android:text="Start Scanner"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

   <ImageView
       android:id="@+id/page_preview"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1" />
</LinearLayout>

To actually show the image, let’s implement a processPagePreview method. We can take the image from the pageFileStorage by a pageId. Alternatively, we could access the file by URI using the getImageURI method. 

In real applications, there would be more efficient ways of loading images from storage (Glide or Picasso libraries, for example).

private fun processPagePreview(page: Page) {
        val imageType = PageFileStorage.PageFileType.DOCUMENT
        val filteredPreviewImage = pageFileStorage.getImage(page.pageId, imageType)
        val previewWidget = findViewById<ImageView>(R.id.page_preview)
        previewWidget.setImageBitmap(filteredPreviewImage)
    }

A DOCUMENT image type is the cropped and perspective-corrected image (hi-resolution), while the ORIGINAL image is the unprocessed image (hi-resolution). All images are stored as JPG files by default. It’s also possible to access preview images (low-resolution), which should be used as thumbnails and can be displayed by calling getPreviewImage. See the API docs for more details.

‍Finally, let’s run our app once more. The cropped image preview now appears in the ImageView after a successful scan. Congratulations!

Complete example projects

To get off to a flying start, check out the following example project on GitHub:

Scanbot SDK example project

This project demonstrates the integration of all API methods of the Scanbot SDK for Android, such as Cropping UI, Image Filtering, PDF and TIFF Rendering, Optical Character Recognition, Barcode Scanning, MRZ Scanner and EHIC Scanning. If you want more control over how the scanning screen looks, or more flexibility in the implementation, you can use our Classical Components. You can find an example project for these components here.

Finally, you can find the app we made in this tutorial here.

Scanbot SDK (trial) license key

Please note: Without a license key, the Scanbot SDK will run for one minute per session! After the trial period is up, all Scanbot Scanner SDK functions as well as the UI components will stop working or may be terminated. 

You can get an unrestricted, no-strings-attached 7-day trial license for free.

As the Scanbot SDK license key is bound to the Application ID, you will have to use the default generated App Identifier (io.example.myapplication) of this tutorial app. Alternatively, you can use the ID of your app when requesting your trial license. Please note that app IDs are case-sensitive!

Where to find the App Identifier

The application ID is defined in the app/build.gradle file as applicationId property:

android {
    defaultConfig {
        applicationId "io.example.myapplication"
        ...
    }
    ...
}

Happy coding! 👩💻👨💻
If you need any kind of assistance, feel free to contact one of our experts. Let’s talk.

💡 You can also follow along our tutorial on YouTube!

💡 Did you have trouble with this tutorial?

We offer free integration support for the implementation and testing of the Scanbot SDK. If you encounter technical issues or need advice on choosing the appropriate framework or features, join our Slack or MS Teams or send us an email to receive your free support.