QR Code has became very popular in India. Even small vendors accept digital payments, and we use any Payment App (Google Pay, PhonePe, PayTm) to scan a QR Code and pay them easily. And in your next android app project, you might want to add a QR Code Scanner. And that is why I am here with QR Code Reader Android Kotlin Tutorial.
In this tutorial we will use ML Kit to build a QR Code Scanner Application. If you want to know about what exactly we are going to build, then watch the video below.
You can actually put any kind of information in a QR Code; and for this example I’ve used URL.
Table of Contents
What is QR Code?
As we already discussed, almost all digital app based payments in India uses QR Code; so I assume you’ve seen a QR Code already. It stands for Quick Response Code. QR Code is a type of Barcode; it is a two dimensional Bar Code. It contains black squares over a white background, and we can put some data in it that can be read by camera.
Below you can see a sample QR Code, and you can use this QR Code to test the application that you will build in this tutorial.
Building a QR Code Reader Android App
Now let’s build our application that can read information from a QR Code.
Pre-requisites
I will be using the following things, for this project.
- ML-Kit Barcode Scanning – To scan the QR Code
- Bottom Sheet – To display the information after reading from QR Code
- CameraX – For creating camera that will read QR Code
- Jsoup – To read URL Meta Data
Android Studio Project
Now let’s create our project. I have created a new project using an Empty Activity and the first step is adding required dependencies.
- Open app level build.gradle file and add the following dependencies.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//ML Kit Barcode Scanning implementation 'com.google.mlkit:barcode-scanning:16.0.3' //CameraX Dependencies implementation "androidx.camera:camera-core:1.0.0-beta10" implementation "androidx.camera:camera-camera2:1.0.0-beta10" implementation "androidx.camera:camera-lifecycle:1.0.0-beta10" implementation "androidx.camera:camera-view:1.0.0-alpha10" implementation "androidx.camera:camera-extensions:1.0.0-alpha10" implementation 'com.google.android.material:material:1.3.0-alpha02' //Library to get URL Meta Data implementation 'org.jsoup:jsoup:1.13.1' |
- We also need to add Java8 compileOptions; so add the following inside android block.
1 2 3 4 5 6 |
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } |
- Now you can sync the project.
Designing App’s UI
Scanner
We need camera that will scan the QR Code and I will create it inside MainActivity .
I’ve created the following UI Design.
To create the design like this, you can use the following XML code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000000"> <androidx.camera.view.PreviewView android:id="@+id/previewView" android:layout_width="match_parent" android:layout_height="match_parent" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="140dp" android:text="QR Code Reader Android\nusing ML Kit" android:textAlignment="center" android:textColor="#FAFAFA" android:textSize="24sp" /> <ImageView android:layout_width="300dp" android:layout_height="350dp" android:layout_below="@id/text_view" android:layout_centerHorizontal="true" android:layout_marginTop="34dp" android:background="@drawable/background_rect" /> </RelativeLayout> </FrameLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> |
Everything is very simple and straightforward, you just need to see the <androidx.camera.view.PreviewView /> here, because I think this might be a new thing for you if you have not already used CameraX.
PreviewView is for our Camera Preview.
Bottom Sheet
Once we have scanned the QR Code and got the information from it; we will show the information to a BottomSheet. And below is the design of my BottomSheet.
For bottom sheet I’ve created a layout file named bottom_sheet_barcode_data.xml .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/bottom_sheet" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/text_view_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="8dp" android:textAlignment="center" android:textAppearance="@style/TextAppearance.AppCompat.Title" android:textColor="#2B2B2B" tools:text="Simplified Coding - QR Code Reader" /> <TextView android:id="@+id/text_view_desc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="8dp" android:textAlignment="center" android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textColor="#6C6C6C" android:textSize="10sp" tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sed fermentum odio. Donec laoreet nisi nisl. Praesent at mauris enim. Suspendisse metus quam, congue eu nunc a, lacinia placerat orci" /> <TextView android:id="@+id/text_view_link" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAlignment="center" android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textColor="#002865" tools:text="https://www.youtube.com/SimplifiedCoding?sub_confirmation=1" /> <TextView android:id="@+id/text_view_visit_link" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="12dp" android:background="#00A5E6" android:paddingLeft="12dp" android:paddingTop="4dp" android:paddingRight="12dp" android:paddingBottom="4dp" android:text="Visit Link" android:textColor="#ffffff" /> </LinearLayout> |
We have all the required designs ready now.
Building Camera
The first thing that we need in this app is the Camera. Because camera will read the QR Code. So let’s build the camera.
Camera Permission
- Add Camera Permission in AndroidManifest.xml.
1 2 3 4 |
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.INTERNET" /> |
I’ve also added the internet permission, because we need it to fetch URL content.
- To make things smaller, I am not asking Camera Permission in the code, but I will directly open the settings page, if the Camera Permission is not given.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private fun checkCameraPermission() { if (ContextCompat.checkSelfPermission( this, Manifest.permission.CAMERA ) != PackageManager.PERMISSION_GRANTED ) { Intent().also { it.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS it.data = Uri.fromParts("package", packageName, null) startActivity(it) finish() } } } |
- It is not a good practice to do, so I would like you all to add the run time permission code in your project.
- We will call the above checkCameraPermission() function inside the onCreate() of MainActivity .
Camera
Now let’s finally build our camera.
- First we need to define these objects.
1 2 3 4 |
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider> private lateinit var cameraExecutor: ExecutorService |
- Then we will initialize these inside onCreate() function.
1 2 3 4 |
cameraExecutor = Executors.newSingleThreadExecutor() cameraProviderFuture = ProcessCameraProvider.getInstance(this) |
- Now we can get the cameraProvider, from cameraProviderFuture and then we can bind the camera preview to our PreviewView.
1 2 3 4 5 6 |
cameraProviderFuture.addListener(Runnable { val cameraProvider = cameraProviderFuture.get() bindPreview(cameraProvider) }, ContextCompat.getMainExecutor(this)) |
- Here is the bindPreview() function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@SuppressLint("UnsafeExperimentalUsageError") private fun bindPreview(cameraProvider: ProcessCameraProvider) { val preview: Preview = Preview.Builder() .build() val cameraSelector: CameraSelector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK) .build() preview.setSurfaceProvider(previewView.createSurfaceProvider(null)) cameraProvider.bindToLifecycle( this as LifecycleOwner, cameraSelector, preview ) } |
Now you can run your application and you will see the camera preview.
Scanning QR Code
We have the camera and now it is the time to check if our camera is pointed to a QR Code or not. Basically we want to read if there is a QR Code to read.
- To achieve this thing, we need to create an ImageAnalysis.Analyzer .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
class MyImageAnalyzer: ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { scanBarcode(imageProxy) } @SuppressLint("UnsafeExperimentalUsageError") private fun scanBarcode(imageProxy: ImageProxy) { imageProxy.image?.let { image -> val inputImage = InputImage.fromMediaImage(image, imageProxy.imageInfo.rotationDegrees) val scanner = BarcodeScanning.getClient() scanner.process(inputImage) .addOnCompleteListener { imageProxy.close() if (it.isSuccessful) { readBarcodeData(it.result as List<Barcode>) } else { it.exception?.printStackTrace() } } } } private fun readBarcodeData(barcodes: List<Barcode>) { for (barcode in barcodes) { when (barcode.valueType) { //you can check if the barcode has other values //For now I am using it just for URL Barcode.TYPE_URL -> { //we have the URL here val url = barcode.url?.url } } } } } |
- Now, we have the URL that we read from the QR Code. We just need to show it. You can display it anywhere you want. I have used BottomSheet for it.
- But before going to BottomSheet, we need to add this Analyzer to our Camera. We will do it inside bindPreview() function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(cameraExecutor, analyzer) cameraProvider.bindToLifecycle( this as LifecycleOwner, cameraSelector, imageAnalysis, preview ) |
- First we have create an ImageAnalysis , using ImageAnalysis.Builder() and then we set the analyzer to it.
- analyzer is the instance of MyImageAnalyzer that we just created.
- Finally we passed the imageAnalysis as the third parameter to bindToLifecycle() function.
Displaying QR Code in BottomSheet
- The bottom sheet design we have already created. And here is the class to display the URL in the bottom sheet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class BarcodeResultBottomSheet : BottomSheetDialogFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? = inflater.inflate(R.layout.bottom_sheet_barcode_data, container, false) //We will call this function to update the URL displayed fun updateURL(url: String) { fetchUrlMetaData(url) { title, desc -> view?.apply { findViewById<TextView>(R.id.text_view_title)?.text = title findViewById<TextView>(R.id.text_view_desc)?.text = desc findViewById<TextView>(R.id.text_view_link)?.text = url findViewById<TextView>(R.id.text_view_visit_link).setOnClickListener { _ -> Intent(Intent.ACTION_VIEW).also { it.data = Uri.parse(url) startActivity(it) } } } } } //this function will fetch URL data private fun fetchUrlMetaData( url: String, callback: (title: String, desc: String) -> Unit ) { val executor = Executors.newSingleThreadExecutor() val handler = Handler(Looper.getMainLooper()) executor.execute { val doc = Jsoup.connect(url).get() val desc = doc.select("meta[name=description]")[0].attr("content") handler.post { callback(doc.title(), desc) } } } } |
- Now we just need to use this BottomSheet in our Image Analyzer class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class MyImageAnalyzer( private val fragmentManager: FragmentManager ) : ImageAnalysis.Analyzer { private var bottomSheet = BarcodeResultBottomSheet() override fun analyze(imageProxy: ImageProxy) { scanBarcode(imageProxy) } @SuppressLint("UnsafeExperimentalUsageError") private fun scanBarcode(imageProxy: ImageProxy) { imageProxy.image?.let { image -> val inputImage = InputImage.fromMediaImage(image, imageProxy.imageInfo.rotationDegrees) val scanner = BarcodeScanning.getClient() scanner.process(inputImage) .addOnCompleteListener { imageProxy.close() if (it.isSuccessful) { readBarcodeData(it.result as List<Barcode>) } else { it.exception?.printStackTrace() } } } } private fun readBarcodeData(barcodes: List<Barcode>) { for (barcode in barcodes) { when (barcode.valueType) { Barcode.TYPE_URL -> { if (!bottomSheet.isAdded) bottomSheet.show(fragmentManager, "") bottomSheet.updateURL(barcode.url?.url.toString()) } } } } } |
- You just need to pass fragmentManager while creating analyzer in MainActivity .
And that’s it, you have successfully built your QR Code Reader Android Application using ML Kit.
QR Code Reader Android Kotlin Source Code
In case you are still confused or having some problem while building your QR Code Reader Android App, then here is my source code that is perfectly working.
So that is all for this QR Code Reader tutorial friends. I hope you enjoyed the tutorial and built your own QR Code Reader App. Feel free to leave your comments below if you have any query or question. Thank You ♥️