Android QR Code Scanner in Kotlin
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.
- 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