Membuat Aplikasi QR Code Scanner menggunakan Kotlin

Membuat aplikasi QR Code Scanner menggunakan kotlin

Halo semuanya, pada postingan ini saya akan membagikan tutorial sederhana untuk kalian yang baru belajar membuat aplikasi android menggunakan android studio. Pada tutorial sebelumnya kita telah belajar bagaimana membuat aplikasi QR Code generator dengan menggunakan library QRGen. Nah, pada tutorial kali ini kita akan belajar bagaimana membuat aplikasi QR Code scanner dengan menggunakan kotlin. Aplikasi QR Code scanner yang akan kita buat ini dapat melakukan scan QR Code dengan 2 (dua) cara. Cara pertama yaitu dengan scan secara langsung melalui kamera sedangkan cara kedua yaitu dengan upload gambar QR Code dari galeri.

Project QRCodeScanner

  • Hal pertama yang harus kita lakukan tentu membuat project baru di android studio. Pada tutorial ini saya memberikan nama projeknya “QRCodeScanner”.

Update AndroidManifest.xml

  • Pada file AndroidManifest.xml, tambahkan beberapa kode seperti berikut
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.tubianto.qrcodescanner">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<application
android:networkSecurityConfig="@xml/network_security_config"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.QRCodeScanner">
<activity
android:hardwareAccelerated="true"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="screenOrientation" />
</application>
</manifest>

Update Gradle

  • Implementasi library untuk QR Code Scanner menggunakan kamera
// QR Code Scanner
implementation('com.journeyapps:zxing-android-embedded:4.2.0') { transitive = false }
implementation 'com.google.zxing:core:3.3.0' // For Android SDK versions < 24, Android 14+ support
allprojects {
repositories {
google()
mavenCentral()
}
}

Desain User Interface

  • Buka activity_main.xml, lalu ubah kodenya seperti berikut
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:gravity="center"
android:text="Result"
android:textAlignment="center"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:gravity="center"
android:text="..."
android:textAlignment="center"
android:textSize="32sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_text" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:onClick="clickScan"
android:text="Scan QR Code"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Cek Permission

  • Cek apakah user sudah mengizinkan semua permission
private fun checkPermission(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
when (PackageManager.PERMISSION_DENIED) {
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) -> {
requestPermissions(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
WRITE_EXTERNAL_STORAGE_PERMISSION_CODE
)
}
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) -> {
requestPermissions(
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
READ_EXTERNAL_STORAGE_PERMISSION_CODE
)
}
checkSelfPermission(Manifest.permission.CAMERA) -> {
requestPermissions(
arrayOf(Manifest.permission.CAMERA),
CAMERA_PERMISSION_CODE
)
}
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
WRITE_EXTERNAL_STORAGE_PERMISSION_CODE -> if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "Anda perlu memberikan semua izin untuk menggunakan aplikasi ini.", Toast.LENGTH_SHORT).show()
finish()
}
}
READ_EXTERNAL_STORAGE_PERMISSION_CODE -> if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "Anda perlu memberikan semua izin untuk menggunakan aplikasi ini.", Toast.LENGTH_SHORT).show()
finish()
}
}
CAMERA_PERMISSION_CODE -> if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED){
Toast.makeText(this, "Anda perlu memberikan semua izin untuk menggunakan aplikasi ini.", Toast.LENGTH_SHORT).show()
finish()
}
}
}
}

Membuat dialog pilihan metode scan

  • Ketika tombol “Scan QR Code” di tekan, maka akan muncul dialog pilihan metode scan melalui kamera atau galeri.
fun clickScan(view: View) {
val scanOptions = arrayOf<String>("Camera", "Gallery")
AlertDialog.Builder(this)
.setTitle("Scan QR Code melalui")
.setItems(scanOptions) { _, which-> when (which) {
0 -> openCamera()
1 -> openGallery()
} }
.create()
.show()
}

Scan QR Code dari kamera dan menampilkan hasilnya

  • Berikut kode untuk scan QR Code dengan kamera dan menampilkan hasil scannya pada textview
private fun openCamera() {
val qrScan = IntentIntegrator(this)
qrScan.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
qrScan.setPrompt("Scan a QR Code")
qrScan.setOrientationLocked(false)
qrScan.setBeepEnabled(true)
qrScan.setBarcodeImageEnabled(true)
//initiating the qr code scan
qrScan.initiateScan()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (result != null)
{
//if qrcode has nothing in it
if (result.contents == null){
Toast.makeText(this, "Result Not Found", Toast.LENGTH_LONG).show()
} else {
//if qr contains data
try {
val contents = result.contents
tvResult.text = contents
}
catch (e: JSONException) {
e.printStackTrace()
//if control comes here
//that means the encoded format not matches
//in this case you can display whatever data is available on the qrcode
//to a toast
Toast.makeText(this, result.contents, Toast.LENGTH_LONG).show()
}
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}

Scan QR Code dari galeri dan menampilkan hasilnya

  • Berikut kode untuk scan QR Code dari galeri dan menampilkan hasil scannya pada textview
private fun openGallery() {
val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
resultLauncherGallery.launch(galleryIntent)
}
var resultLauncherGallery = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
val imageUri = data!!.data!!
val imagePath = convertMediaUriToPath(imageUri)
val imgFile = File(imagePath)
scanImageQRCode(imgFile)
} else {
Toast.makeText(this, "Result Not Found", Toast.LENGTH_LONG).show()
}
}
private fun convertMediaUriToPath(uri: Uri):String {
val proj = arrayOf<String>(MediaStore.Images.Media.DATA)
val cursor = contentResolver.query(uri, proj, null, null, null)
val columnIndex = cursor!!.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
cursor.moveToFirst()
val path = cursor.getString(columnIndex)
cursor.close()
return path
}
private fun scanImageQRCode(file: File){
val inputStream: InputStream = BufferedInputStream(FileInputStream(file))
val bitmap = BitmapFactory.decodeStream(inputStream)
val decoded = scanQRImage(bitmap)
Log.i("QrTest", "Decoded string=$decoded")
}
private fun scanQRImage(bMap: Bitmap): String? {
var contents: String? = null
val intArray = IntArray(bMap.width * bMap.height)
//copy pixel data from the Bitmap into the 'intArray' array
bMap.getPixels(intArray, 0, bMap.width, 0, 0, bMap.width, bMap.height)
val source: LuminanceSource = RGBLuminanceSource(bMap.width, bMap.height, intArray)
val bitmap = BinaryBitmap(HybridBinarizer(source))
val reader: Reader = MultiFormatReader()
try {
val result: Result = reader.decode(bitmap)
contents = result.text
tvResult.text = contents
} catch (e: Exception) {
Log.e("QrTest", "Error decoding qr code", e)
Toast.makeText(this, "Error decoding QR Code, Mohon pilih gambar QR Code yang benar!", Toast.LENGTH_SHORT).show()
}
return contents
}

Kode lengkap MainActivity.kt

  • Berikut kode lengkap MainActivity.kt
package com.tubianto.qrcodescanner
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.google.zxing.*
import com.google.zxing.common.HybridBinarizer
import com.google.zxing.integration.android.IntentIntegrator
import org.json.JSONException
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
/**
* Created by Tubianto on 18/06/2021.
*/
class MainActivity : AppCompatActivity() {
private lateinit var tvResult: TextView
private var WRITE_EXTERNAL_STORAGE_PERMISSION_CODE: Int = 1
private var READ_EXTERNAL_STORAGE_PERMISSION_CODE: Int = 2
private var CAMERA_PERMISSION_CODE: Int = 3
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
checkPermission()
}
private fun init(){
tvResult = findViewById(R.id.tv_result)
}
private fun checkPermission(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
when (PackageManager.PERMISSION_DENIED) {
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) -> {
requestPermissions(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
WRITE_EXTERNAL_STORAGE_PERMISSION_CODE
)
}
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) -> {
requestPermissions(
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
READ_EXTERNAL_STORAGE_PERMISSION_CODE
)
}
checkSelfPermission(Manifest.permission.CAMERA) -> {
requestPermissions(
arrayOf(Manifest.permission.CAMERA),
CAMERA_PERMISSION_CODE
)
}
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
WRITE_EXTERNAL_STORAGE_PERMISSION_CODE -> if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "Anda perlu memberikan semua izin untuk menggunakan aplikasi ini.", Toast.LENGTH_SHORT).show()
finish()
}
}
READ_EXTERNAL_STORAGE_PERMISSION_CODE -> if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "Anda perlu memberikan semua izin untuk menggunakan aplikasi ini.", Toast.LENGTH_SHORT).show()
finish()
}
}
CAMERA_PERMISSION_CODE -> if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED){
Toast.makeText(this, "Anda perlu memberikan semua izin untuk menggunakan aplikasi ini.", Toast.LENGTH_SHORT).show()
finish()
}
}
}
}
fun clickScan(view: View) {
val scanOptions = arrayOf<String>("Camera", "Gallery")
AlertDialog.Builder(this)
.setTitle("Scan QR Code melalui")
.setItems(scanOptions) { _, which-> when (which) {
0 -> openCamera()
1 -> openGallery()
} }
.create()
.show()
}
private fun openCamera() {
val qrScan = IntentIntegrator(this)
qrScan.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
qrScan.setPrompt("Scan a QR Code")
qrScan.setOrientationLocked(false)
qrScan.setBeepEnabled(true)
qrScan.setBarcodeImageEnabled(true)
//initiating the qr code scan
qrScan.initiateScan()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (result != null)
{
//if qrcode has nothing in it
if (result.contents == null){
Toast.makeText(this, "Result Not Found", Toast.LENGTH_LONG).show()
} else {
//if qr contains data
try {
val contents = result.contents
tvResult.text = contents
}
catch (e: JSONException) {
e.printStackTrace()
//if control comes here
//that means the encoded format not matches
//in this case you can display whatever data is available on the qrcode
//to a toast
Toast.makeText(this, result.contents, Toast.LENGTH_LONG).show()
}
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private fun openGallery() {
val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
resultLauncherGallery.launch(galleryIntent)
}
var resultLauncherGallery = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
val imageUri = data!!.data!!
val imagePath = convertMediaUriToPath(imageUri)
val imgFile = File(imagePath)
scanImageQRCode(imgFile)
} else {
Toast.makeText(this, "Result Not Found", Toast.LENGTH_LONG).show()
}
}
private fun convertMediaUriToPath(uri: Uri):String {
val proj = arrayOf<String>(MediaStore.Images.Media.DATA)
val cursor = contentResolver.query(uri, proj, null, null, null)
val columnIndex = cursor!!.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
cursor.moveToFirst()
val path = cursor.getString(columnIndex)
cursor.close()
return path
}
private fun scanImageQRCode(file: File){
val inputStream: InputStream = BufferedInputStream(FileInputStream(file))
val bitmap = BitmapFactory.decodeStream(inputStream)
val decoded = scanQRImage(bitmap)
Log.i("QrTest", "Decoded string=$decoded")
}
private fun scanQRImage(bMap: Bitmap): String? {
var contents: String? = null
val intArray = IntArray(bMap.width * bMap.height)
//copy pixel data from the Bitmap into the 'intArray' array
bMap.getPixels(intArray, 0, bMap.width, 0, 0, bMap.width, bMap.height)
val source: LuminanceSource = RGBLuminanceSource(bMap.width, bMap.height, intArray)
val bitmap = BinaryBitmap(HybridBinarizer(source))
val reader: Reader = MultiFormatReader()
try {
val result: Result = reader.decode(bitmap)
contents = result.text
tvResult.text = contents
} catch (e: Exception) {
Log.e("QrTest", "Error decoding qr code", e)
Toast.makeText(this, "Error decoding QR Code, Mohon pilih gambar QR Code yang benar!", Toast.LENGTH_SHORT).show()
}
return contents
}
}

Screenshot

Pilihan metode scan
Scan dari kamera
Hasil scan “tubianto.com”

Download source code

Terima kasih

You May Also Like

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *