In this tutorial, we’ll use Android Studio and the Scanbot Text Pattern Scanner SDK to create an app for reading serial numbers from a live camera stream, which can then be processed further.

We’ll achieve this by following these steps:
- Preparing the project
- Initializing the SDK
- Setting up the main screen
- Implementing the number scanning feature
- Setting the desired string using regular expressions
All you need is the latest version of Android Studio and you’re good to go.
Want to see the final code right away? Click here.
MainActivity.kt:
package com.example.androidserialnumberscanner
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import io.scanbot.sdk.textpattern.*
import io.scanbot.sdk.ui_v2.textpattern.*
import io.scanbot.sdk.ui_v2.textpattern.configuration.*
import java.util.regex.Pattern
class MainActivity : AppCompatActivity() {
val resultLauncher: ActivityResultLauncher<TextPatternScannerScreenConfiguration> =
registerForActivityResult(TextPatternScannerActivity.ResultContract()) { resultEntity: TextPatternScannerActivity.Result ->
if (resultEntity.resultOk) {
resultEntity.result?.rawText?.let {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
findViewById<Button>(R.id.btn_scan_serialnumber).setOnClickListener {
val configuration = TextPatternScannerScreenConfiguration()
configuration.scannerConfiguration.validator = CustomContentValidator().apply {
val pattern = Pattern.compile("^[0-9]{11}$")
this.callback = object : ContentValidationCallback {
override fun clean(rawText: String): String {
return rawText.replace(" ", "")
}
override fun validate(text: String): Boolean {
val matcher = pattern.matcher(text)
return matcher.find()
}
}
}
resultLauncher.launch(configuration)
}
}
}
Step 1: Prepare the project
Create a new Empty Views Activity and name the project (e.g., “Android Serial Number Scanner”).
When your project is ready, go to settings.gradle.kts and add the Maven repositories for our SDK:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// Add the repositories here:
maven(url = "https://nexus.scanbot.io/nexus/content/repositories/releases/")
maven(url = "https://nexus.scanbot.io/nexus/content/repositories/snapshots/")
}
}
Now go to app/build.gradle.kts and add the dependencies for the Scanbot SDK and the RTU UI:
dependencies {
implementation("io.scanbot:sdk-package-2:7.0.0")
implementation("io.scanbot:sdk-textpattern-assets:7.0.0")
implementation("io.scanbot:rtu-ui-v2-bundle:7.0.0")
Sync the project.
💡 We use Scanbot SDK version 7.0.0 in this tutorial. You can find the latest version in the changelog.
We need to access the device camera to scan serial numbers. Therefore, add the necessary permissions in AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
// Add the permissions here:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<application
// ...
Step 2: Initialize the SDK
Before we can use the Scanbot Text Pattern Scanner SDK, we need to initialize it. The recommended approach is to do it in your Application
implementation. This ensures the SDK is correctly initialized even when the app‘s process is restored after being terminated in the background.
First, we need to create an Application
subclass by right-clicking on the folder app/kotlin+java/com.example.androidserialnumberscanner, selecting New > Kotlin Class/File, and naming it (e.g. “ExampleApplication”).
In the resulting ExampleApplication.kt, let’s first add the necessary imports.
import android.app.Application
import io.scanbot.sdk.ScanbotSDKInitializer
Then we make ExampleApplication
extend the Application
class by adding : Application()
and putting the code for initializing the SDK inside it.
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
ScanbotSDKInitializer()
// Optional: Uncomment the next line if you have a license key.
// .license(this, LICENSE_KEY)
.initialize(this)
}
}
💡 Without a license key, our SDK only runs for 60 seconds per session. This is more than enough for the purposes of our tutorial, but if you like, you can generate a license key. Just make sure to change your applicationId
in app/build.gradle.kts to io.scanbot.androiddocumentscanner
and use that ID for generating the license.
Finally, we need to register the ExampleApplication
class in AndroidManifest.xml.
<application
android:name=".ExampleApplication"
// ...
Step 3: Set up the main screen
We’re going to build a rudimentary UI so we can quickly start the Text Pattern Scanner from the main screen.
For this tutorial, we’re going to go with a single-button layout, which you can copy and paste into app/res/layout/activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_scan_serialnumber"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:text="Scan Serial Number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Next, we’ll connect the button to our RTU UI’s scanning screen.
Step 4: Implement the number scanning feature
Go to MainActivity.kt and add the necessary imports.
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import io.scanbot.sdk.textpattern.*
import io.scanbot.sdk.ui_v2.textpattern.*
import io.scanbot.sdk.ui_v2.textpattern.configuration.*
import java.util.regex.Pattern // We'll need this class in step 5.
In the MainActivity
class, add a private property that we’ll use to launch the Text Pattern Scanner. We’ll also extract the raw text from the scan result and display it to the user in a Toast message.
class MainActivity : AppCompatActivity() {
val resultLauncher: ActivityResultLauncher<TextPatternScannerScreenConfiguration> =
registerForActivityResult(TextPatternScannerActivity.ResultContract()) { resultEntity: TextPatternScannerActivity.Result ->
if (resultEntity.resultOk) {
resultEntity.result?.rawText?.let {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
In the onCreate()
method, set up an OnClickListener
for the Scan Serial Number button. When the button is clicked, it starts the resultLauncher
using a configuration object.
// ... paste after the existing code in onCreate():
findViewById<Button>(R.id.btn_scan_serialnumber).setOnClickListener {
val configuration = TextPatternScannerScreenConfiguration()
// Code from step 5 will go here.
// Start the recognizer activity.
resultLauncher.launch(configuration)
}
Feel free to build and run the app to check if everything is working so far.
Step 5: Set the desired string using regular expressions
As it is now, our scanner will scan any string of characters. This can be useful, but if you only want to extract specific kinds of data, you’ll need to pre-define the string structure in advance.
For example, if we know that the serial numbers we’re looking for are 11 digits long, we can configure the scanner like this:
configuration.scannerConfiguration.validator = CustomContentValidator().apply {
// Define the expected structure of the string using regex.
val pattern = Pattern.compile("^[0-9]{11}$")
this.callback = object : ContentValidationCallback {
override fun clean(rawText: String): String {
return rawText.replace(" ", "")
}
override fun validate(text: String): Boolean {
val matcher = pattern.matcher(text)
return matcher.find()
}
}
}
Using regular expressions allows us to pre-define exactly what kind of character sequences we’re looking for. This way, we can ensure that only data structures adhering to that pattern are extracted.
Our final MainActivity.kt will then look like this:
package com.example.androidserialnumberscanner
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import io.scanbot.sdk.textpattern.*
import io.scanbot.sdk.ui_v2.textpattern.*
import io.scanbot.sdk.ui_v2.textpattern.configuration.*
import java.util.regex.Pattern
class MainActivity : AppCompatActivity() {
val resultLauncher: ActivityResultLauncher<TextPatternScannerScreenConfiguration> =
registerForActivityResult(TextPatternScannerActivity.ResultContract()) { resultEntity: TextPatternScannerActivity.Result ->
if (resultEntity.resultOk) {
resultEntity.result?.rawText?.let {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
findViewById<Button>(R.id.btn_scan_serialnumber).setOnClickListener {
val configuration = TextPatternScannerScreenConfiguration()
configuration.scannerConfiguration.validator = CustomContentValidator().apply {
val pattern = Pattern.compile("^[0-9]{11}$")
this.callback = object : ContentValidationCallback {
override fun clean(rawText: String): String {
return rawText.replace(" ", "")
}
override fun validate(text: String): Boolean {
val matcher = pattern.matcher(text)
return matcher.find()
}
}
}
resultLauncher.launch(configuration)
}
}
}
Now build and run the app and scan a serial number …

… to test your scanner!

Congratulations! You’ve successfully integrated a highly flexible serial number scanner into your Android app 🎉
If you like, you can further customize the scanning interface to suit your preferences. Head over to the SDK’s documentation to learn more.
Happy scanning! 🤳
Conclusion
If this tutorial has piqued your interest in integrating data capture functionalities into your Android app, make sure to take a look at the SDK’s other neat features in the Android Document Scanner SDK’s documentation – or run our example project for a more hands-on experience.
Should you have questions about this tutorial or run into any issues, we’re happy to help! Just shoot us an email via tutorial-support@scanbot.io.