Search code examples
androidandroid-jetpack-composeandroid-jetpackdevelopment-environment

Adding Small Square Focus to QR Code Scanner in Jetpack Compose


I have successfully implemented a QR Code Scanner in Jetpack Compose, but I would like to enhance the user experience by adding a small square focus that hovers over the icon of the QR code. This square focus will visually indicate to the user where to position the QR code for scanning. How can I achieve this effect and integrate it into my existing QR Code Scanner implementation?

By implementing the changes in the provided code, I would expect to see a small red square overlay on top of the camera preview, indicating the focus area for the QR code.Due to errors in the previous code implementation, the small square overlay was not drawn correctly on top of the camera preview, and the compilation errors prevented the code from functioning as intended.


Solution

  • You could try out Google Code Scanner Library https://developers.google.com/ml-kit/vision/barcode-scanning/code-scanner

    below implementation might help you.

    QRCodeScanner.kt
    -----------------
    
    import kotlinx.coroutines.flow.Flow
    
    interface QRCodeScanner {
    
        fun startScanning(): Flow<String?>
    }
    
    QRCodeScannerImpl.kt
    --------------------
    
    import com.google.android.gms.common.moduleinstall.ModuleInstallClient
    import com.google.android.gms.common.moduleinstall.ModuleInstallRequest
    import com.google.mlkit.vision.barcode.common.Barcode
    import com.google.mlkit.vision.codescanner.GmsBarcodeScanner
    import com.niyaj.popos.features.qrcode_scanner.domain.repository.QRCodeScanner
    import kotlinx.coroutines.channels.awaitClose
    import kotlinx.coroutines.flow.Flow
    import kotlinx.coroutines.flow.callbackFlow
    import kotlinx.coroutines.launch
    import timber.log.Timber
    
    class QRCodeScannerImpl(
        private val scanner: GmsBarcodeScanner,
        private val playModule: ModuleInstallClient,
    ) : QRCodeScanner {
    
        init {
            /** Checking does ScannerModule is installed on the device otherwise,
            install using ModuleClientAPI, for more visit 
            https://developers.google.com/android/guides/module-install-apis **/
    
            playModule
                .areModulesAvailable(scanner)
                .addOnSuccessListener {
                    if (!it.areModulesAvailable()) {
                        Timber.d("Downloading QR Code Module")
                        // Modules are not present on the device install...
    //                    playModule.deferredInstall(scanner)
                        val newRequest = ModuleInstallRequest.newBuilder().addApi(scanner).build()
                        playModule.installModules(newRequest)
                    }
                }.addOnFailureListener {
                    Timber.d("Failed to install QRCodeScanner Module")
                }
        }
    
        override fun startScanning() : Flow<String?> {
            return callbackFlow {
                scanner.startScan()
                    .addOnSuccessListener {
                        launch {
                            send(getDetails(it))
                        }
                    }.addOnFailureListener {
                        launch {
                            send(null)
                        }
                    }
                awaitClose {  }
            }
        }
    
        private fun getDetails(barcode : Barcode) : String? {
            return when (barcode.valueType) {
                Barcode.TYPE_WIFI -> {
                    val ssid = barcode.wifi!!.ssid
                    val password = barcode.wifi!!.password
                    val type = barcode.wifi!!.encryptionType
                    "ssid : $ssid, password : $password, type : $type"
                }
    
                Barcode.TYPE_URL -> {
                    "${barcode.url?.url}"
                }
    
                Barcode.TYPE_PRODUCT -> {
                    barcode.displayValue
                }
    
                Barcode.TYPE_EMAIL -> {
                    "${barcode.email?.address}"
                }
    
                Barcode.TYPE_CONTACT_INFO -> {
                    "${barcode.contactInfo?.name?.formattedName}"
                }
    
                Barcode.TYPE_PHONE -> {
                    "${barcode.phone?.number}"
                }
    
                Barcode.TYPE_CALENDAR_EVENT -> {
                    "${barcode.calendarEvent?.description}"
                }
    
                Barcode.TYPE_GEO -> {
                    "${barcode.geoPoint?.lat} ${barcode.geoPoint?.lng}"
                }
    
                Barcode.TYPE_ISBN -> {
                    barcode.displayValue
                }
    
                Barcode.TYPE_DRIVER_LICENSE -> {
                    "${barcode.driverLicense?.firstName} ${barcode.driverLicense?.lastName }${barcode.driverLicense?.lastName}"
                }
    
                Barcode.TYPE_SMS -> {
                    "${barcode.sms}"
                }
    
                Barcode.TYPE_TEXT -> {
                    barcode.rawValue
                }
    
                Barcode.TYPE_UNKNOWN -> {
                    "${barcode.rawValue}"
                }
    
                else -> {
                    null
                }
            }
        }
    }
    
    
    ScannerModule.kt
    
    import android.app.Application
    import android.content.Context
    import com.google.android.gms.common.moduleinstall.ModuleInstall
    import com.google.android.gms.common.moduleinstall.ModuleInstallClient
    import com.google.mlkit.vision.barcode.common.Barcode
    import com.google.mlkit.vision.codescanner.GmsBarcodeScanner
    import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
    import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
    import com.niyaj.popos.features.qrcode_scanner.data.QRCodeScannerImpl
    import com.niyaj.popos.features.qrcode_scanner.domain.repository.QRCodeScanner
    import dagger.Module
    import dagger.Provides
    import dagger.hilt.InstallIn
    import dagger.hilt.components.SingletonComponent
    
    @Module
    @InstallIn(SingletonComponent::class)
    object ScannerModule {
    
        @Provides
        fun provideContext(app: Application):Context{
            return app.applicationContext
        }
    
        @Provides
        fun provideBarCodeOptions() : GmsBarcodeScannerOptions {
            return GmsBarcodeScannerOptions.Builder()
                .setBarcodeFormats(Barcode.FORMAT_QR_CODE)
                .build()
        }
    
        @Provides
        fun provideGooglePlayModule(context: Context) : ModuleInstallClient {
            return ModuleInstall.getClient(context)
        }
    
        @Provides
        fun provideBarCodeScanner(context: Context, options: GmsBarcodeScannerOptions): GmsBarcodeScanner {
            return GmsBarcodeScanning.getClient(context, options)
        }
    
        @Provides
        fun provideBarCodeScannerRepository(scanner : GmsBarcodeScanner, playModule : ModuleInstallClient): QRCodeScanner {
            return QRCodeScannerImpl(scanner, playModule)
        }
    }