Android QR Code Scanner in Kotlin

Mujeeb ur rahman khan
Towards Dev
Published in
4 min readJun 7, 2023

--

We are going to build simple QR code scanner using third party library based on zxing and firebase to save history of scanned qr code. Zxing is popular library for qr code and bar code scanner in android.

First of all, we need to add barcode scanner dependency in the build.Gradle(app) file.

// qr code
implementation 'com.google.zxing:core:3.4.1'
implementation 'com.journeyapps:zxing-android-embedded:4.2.0'

// firebase
implementation 'com.google.firebase:firebase-database-ktx:20.0.4'
implementation 'com.google.firebase:firebase-auth-ktx'

Now we need to add camera and internet permission in the manifest file.

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

you will find XML layouts and Drawable and other necessary file from here

Code for QRCodeCameraActivity.kt

there is no need to create XML file for QRCodeCameraActivity.kt

package com.example.resolutionai.activity

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import com.example.resolutionai.data.ScannerResult
import com.example.resolutionai.viewmodel.ViewModel

import com.google.zxing.Result
import me.dm7.barcodescanner.zxing.ZXingScannerView


class QRCodeCameraActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {

var scannerView: ZXingScannerView? = null
private lateinit var viewModel: ViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
scannerView = ZXingScannerView(this)
setContentView(scannerView)

setPermission()
variableInit()

}

private fun variableInit() {
viewModel = ViewModelProvider(this)[ViewModel::class.java]

}


private fun setPermission() {
val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)

if (permission != PackageManager.PERMISSION_GRANTED) {
makeRequest()
}
}

private fun makeRequest() {
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.CAMERA),
1
)
}

override fun handleResult(p0: Result?) {
val data = p0!!.text.toString()
val intent = Intent(this, ResultActivity::class.java)

viewModel.addResult(
ScannerResult(
data
)
)

intent.putExtra("result", data)
startActivity(intent)
finish()
}

override fun onResume() {
super.onResume()

scannerView?.setResultHandler(this)
scannerView?.startCamera()
}

override fun onStop() {
super.onStop()
scannerView?.stopCamera()
onBackPressed()
}


override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)

when (requestCode) {
1 -> {
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(
applicationContext,
"You need camera permission",
Toast.LENGTH_SHORT
).show()
}
}
}

}
}

Here is breakdown of whole code.

  1. Setting up Activity
class QRCodeCameraActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
var scannerView: ZXingScannerView? = null
private lateinit var viewModel: ViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
scannerView = ZXingScannerView(this)
setContentView(scannerView)

setPermission()
variableInit()
}
}

The QRCodeCameraActivity class extends AppCompatActivity and implements the ZXingScannerView.ResultHandler interface.
It initializes the scannerView variable with a new instance of ZXingScannerView and sets it as the content view of the activity. The viewModel variable is declared, which will be used to store the scanned QR code results.

2. Variable Initialization

private fun variableInit() {
viewModel = ViewModelProvider(this)[ViewModel::class.java]
}

3. Checking and Requesting Permission

private fun checkPermission() {
val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
if (permission != PackageManager.PERMISSION_GRANTED) {
makeRequest()
}
}

private fun makeRequest() {
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.CAMERA), 1
)
}

the checkPermission method is used to check permissions are granted or not.
if permissions is not granted makeRequest() is used to ask permission to use.

4. Handling the QR Code Result

override fun handleResult(p0: Result?) {
val data = p0!!.text.toString()
val intent = Intent(this, ResultActivity::class.java)

viewModel.addResult(
ScannerResult(data)
)

intent.putExtra("result", data)
startActivity(intent)
finish()
}

handleResult is called when QR code is sucessfuly scanned. It retrives the scanned data and stores it in firebase using addResult
p0!!.text.toString
contains the output of QR Code.
you can customized this function according to your needs.

5. Resuming and Stopping the Camera

override fun onResume() {
super.onResume()

scannerView?.setResultHandler(this)
scannerView?.startCamera()
}

override fun onStop() {
super.onStop()
scannerView?.stopCamera()
onBackPressed()
}

The onResume function is called when the activity is resumed. It sets the QRCodeCameraActivity as the result handler for the scannerView and starts the camera using scannerView?.startCamera(). The onStop function is called when the activity is stopped. It stops the camera.

6. Handling Camera Permission Request

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)

when (requestCode) {
1 -> {
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(
applicationContext,
"You need camera permission",
Toast.LENGTH_SHORT
).show()
}
}
}
}

The onRequestPermissionsResult function is called when the user responds to the camera permission request.
It checks if the permission is granted or not based on the grantResults array.
If the permission is denied, a toast message is displayed indicating the need for camera permission.

Here we have completed work for QR Code scanner.

You can also check additional code to save data in firebase.

Code for Repository. kt

package com.example.resolutionai.repository


import com.example.resolutionai.data.ScannerResult
import com.example.resolutionai.utils.Consts.result
import com.example.resolutionai.utils.FirebaseUtils.database
import com.example.resolutionai.utils.FirebaseUtils.databaseRef
import com.example.resolutionai.utils.FirebaseUtils.firebaseAuth
import com.example.resolutionai.utils.FirebaseUtils.getPhonenumber
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.ValueEventListener

class Repository {

/*
add contact
*/
fun addResult(data: ScannerResult) {
val randomkey = database.reference.push().key!!
databaseRef.child(firebaseAuth.currentUser?.phoneNumber.toString())
.child(randomkey)
.setValue(data)
}


/*
get results
*/
fun getResults(callback: (List<ScannerResult>) -> Unit) {
val resultList = mutableListOf<ScannerResult>()
databaseRef.child(firebaseAuth.currentUser?.phoneNumber.toString()).addValueEventListener(object : ValueEventListener {

override fun onDataChange(snapshot: DataSnapshot) {
resultList.clear()
for (data in snapshot.children) {
val temp = data.getValue(ScannerResult::class.java)!!
resultList.add(temp)
}

callback(resultList)
}

override fun onCancelled(error: DatabaseError) {

}

})

}

}

Code for viewModel.kt

package com.example.resolutionai.viewmodel

import com.example.resolutionai.data.ScannerResult
import com.example.resolutionai.repository.Repository
import com.example.resolutionai.utils.FirebaseUtils.databaseRef
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.ValueEventListener

class ViewModel(private val repository: Repository = Repository()) :
androidx.lifecycle.ViewModel() {

/*
add result
*/
fun addResult(data: ScannerResult) {
repository.addResult(data)
}

/*
get results
*/
fun getResults(callback: (List<ScannerResult>) -> Unit) {
repository.getResults { task ->
callback(task)
}
}
}

check out github repository for source code

Thank you for reading this article. If you found this helpful and interesting, please clap and follow me for more such content.

If I got something wrong, mention it in the comments. I would love to improve.

Connect with me on GitHub and LinkedIn

--

--

I am final year IT engineering student from India. I have deep interest in Android development. on my way to become better developer