Having to insert card details manually into an app could be a pain for users, but thanks to CameraX and MLKit, we can easily add a scanning feature in our app with minimal effort.
CameraX is a Jetpack library to use camera APIs with device compatibility issues already handled for us and with lifecycle support.
The library is based on uses cases and we are going to use two of them: Preview and Image Capture.
For this post, we are going to use the following dependencies:
This use case will allow us to display on-screen what the camera is capturing in real-time.
To do so, we need to add androidx.camera.view.PreviewView to our layout:
Then in code, we can build the Preview use case as follows:
To run our use cases we need a CameraProvider and a CameraSelector, I’ve created some extension using Kotlin Coroutines to get a CameraProvider:
While for the CameraSelector this is all we need:
We can now bind our use cases in this way:
This is another CameraX use case, and the setup is pretty similar to the previous one.
We can create the use case in this way:
It’s time to update our bindUseCases method and also use this new one, but since CameraX is using a callback, and we would like to use kotlin coroutines let’s add a convenient extension method:
Now we can update bindUseCases function like the following:
Now that we have our image is time to extract data from it. For this purpose, we are going to use MLKit Text Recognition.
We need to add this dependency :
and update our Manifest to download the ML model to the device after our app is installed:
Let’s create an ExtractDataUseCase to encapsulate the MLKit integration:
In this use case, we are accepting an instance of TextRecognizer that could be retrieved with
To extract data from our image we first need to transform it into something MLKit can understand. Luckily for us, there is the
InputImage.fromMediaImage(image, rotationDegrees) fatory method designed just for that.
Now that we have an image MLKit can understand we can pass it to our textRecognizer and await the text result.
Once again, we are using Kotlin coroutines so we can use this extension as a bridge from Task (returned by textRecognizer) and our suspend method.
Thanks to CameraX we got the image, and then thanks to MLKit we were able to extract the text on it. Now is our turn to do some minimal logic and pass from raw data to some useful information.
As a first step let’s define what we can extract from the image, and create a data class:
All fields are optional because I have several (debit) cards and on each of them I have a different layout, some of them have the owner on the same side of the number, some of them no.
Same for the expiration date, so ¯_(ツ)_/¯.
Now we can encapsulate our data extraction logic inside a Kotlin Object:
Let’s extract some data:
Here the rationale is to try to get the string that:
- contains at least a space char (between name and surname)
- contains no digits (I think you can’t have them in a name)
If we have more than one match, choose the longest one.
For the number, we are picking the first line that:
- has at lease a space (between numbers)
- has all the chars as digits
Expiration could be something like 08/20 or, 08/2020 this is why I’m choosing the line with
- 5 chars (like 08/20) o 7 chars (like 08/2020)
- ’/’ as third char
Now that we have found the expiration line we need to separate the month from the year
We can now update the activity to use our ExtractDataUseCase
Let’s create it
And then use it
We can now finally bind our result to UI:
That’s all folks! You can find the complete source code at https://github.com/dcampogiani/CreditCardScanner