In the process of Android project development, ZXing is the most commonly used open source library for code scanning. There are numerous optimizations and secondary encapsulation for ZXing on Github. However, the drawback of ZXing is that it only achieves some basic operations for code scanning. Deformations, etc., are not well supported. Now the mainstream approach is to do some optimization based on the source code of Zxing, but the effect is still not ideal, and it will cost a lot of manpower.

Today, we will introduce the perfect alternative to ZXing — Huawei Scan Kit. There is a comprehensive article on the comparison of ZXing and Scan Kit in the forum:

Developer.huawei.com/consumer/cn… Interested readers can read it.

Introduction to the

Huawei Scan Kit provides convenient barcode and QR code scanning, parsing, and generation capabilities, helping developers quickly build in-app code scanning functions.

Thanks to Huawei’s accumulation of capabilities in the field of computer vision, Scan Kit can detect and automatically enlarge remote codes or small codes, and optimize common and complex Scan codes (such as reflective, dark light, defacing, fuzzy, and cylinder) to improve the Scan success rate and user experience.

Scan Kit supports Android and iOS integration. The Android system integrates with Scan Kit to support landscape Scan.

Supported Devices

platform Device type OS version
Android Huawei phones and Huawei tablets EMUI above 3.1
Android Non-huawei phones Android 4.4 or above
iOS Mobile phone IOS 9.0 above

The scene is introduced

scan the code

The Scan Kit can Scan 13 global mainstream code formats. If the developer’s application only deals with some of the specific codes, the developer can also specify the codes in the interface to speed up scanning. Supported code formats:

  • One-dimensional Code: EAN-8, EAN-13, UPC-A, UPC-E, Codabar, Code 39, Code 93, Code 128, ITF-14
  • QR Code: QR Code, Data Matrix, PDF417, Aztec

Scan Kit provides a variety of invocation modes, and developers can select an appropriate mode to build the Scan code function according to their needs.

Call way Support platform Sweep code process Sweep code interface function
Default View Mode Android/iOS Scan Kit processing Scan Kit provides Scan the camera code (you can call Bitmap mode to add the scan function of imported images).
Customized View Mode Android/iOS Scan Kit processing Developer customization Scan the camera code (you can add the scan function of imported images by adding Bitmap Mode).
Bitmap Mode Android/iOS Developer application processing Developer customization Scan code of camera and import picture.
MultiProcessor Mode Android Developer application processing Developer customization Scan codes for cameras and imported images, supporting simultaneous detection of multiple codes.
  • For developers who want to quickly build the powerful scan code function, select Default View Mode or Customized View Mode. In these two modes, Scan Kit directly controls the camera to achieve the optimal Zoom control, adaptive exposure adjustment, adaptive focus adjustment and other operations, ensuring the best scanning experience and reducing the workload of developers. The difference between Default View Mode and Customized View Mode is that the latter allows developers to customize the scan UI.
  • For developers who want to fully customize the scan process and take control of the camera themselves, Bitmap Mode can be used to build your scan function. Bitmap Mode requires developers to control the camera by themselves, and provides two modes, camera scan code and imported image scan code, which can be set when calling the scan code interface. When the user is scanning a small or distant QR code, Scan Kit will return the required multiple of magnification to your application. You only need to adjust the focal length of the camera according to the returned value to quickly obtain the pictures that meet the conditions. Please note that the magnification returned by Scan Kit is calculated based on detection results and user scenarios. It is recommended that you do not change this magnification, otherwise the actual use effect may be reduced.
  • MultiProcessor Mode applies to scenarios where multiple codes need to be scanned at the same time. Because MultiProcessor is less efficient than the other three call methods, you are advised to use the other three call methods if you do not have the above requirements. MultiProcessor Mode Supports synchronous and asynchronous modes. You can select a proper Mode as required.

Parsing code value

Scan Kit returns the original content of the code to the developer. It also analyzes and extracts structured data from qr codes/barcodes encoded in a specific content format to help developers quickly build associated services. The following scenarios are supported: Contact information, Wi-Fi connection information, web pages, calendar, ID card, SMS, phone, email, geographical location, bar code, and ISBN.

Code generation

Scan Kit supports converting strings to one-dimensional codes or qr codes, Currently, the supported Code standards are EAN-8, EAN-13, UPC-A, UPC-E, Codabar, Code 39, Code 93, Code 128, ITF-14, QR Code, Data Matrix, PDF417, and Aztec. The developer only needs to provide the string, code standard and size requirements to obtain the corresponding code diagram.

The integration steps

The steps in the official tutorial [Configuring AppGallery Connect] can be ignored if the application requires a bareback.

Integration with HMS Core SDK

Configure the Maven location of the HMS Core SDK

Open the Android Studio project level “build.gradle” file

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext.kotlin_version = "1.5.0"
    repositories {
        google()
        jcenter()
        // Configure the HMS Core SDK Maven repository address.
        maven {url 'https://developer.huawei.com/repo/'}
    }
    dependencies {
        classpath "Com. Android. Tools. Build: gradle: 2"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        jcenter() 
        // Configure the HMS Core SDK Maven repository address.
        maven {url 'https://developer.huawei.com/repo/'}}}Copy the code

Adding compile dependencies

Open the application-level “build.gradle” file and add the corresponding compilation dependencies. Huawei officially provides us with two types of dependency packages:

The difference is obvious, both the size of the package and the ability to identify. To demonstrate the capabilities of Scan Kit, we choose the scanPlus series with more comprehensive capabilities.

implementation 'com. Huawei. HMS: scanplus: 1.3.2.300'
Copy the code

Application development

Dynamic permission request processing, not separately, interspersed in the code, focuses on four modes of Scan Kit:

  • Default View Mode
  • Customized View Mode
  • Bitmap Mode
  • MultiProcessor Mode

Default View Mode

Default View Mode provides two functions: camera scan code and imported image scan code. It provides a complete Activity and does not require developers to develop the UI of scan code interface. One-click scanning can be enabled to process the scanning results, which is very suitable for general scanning scenarios with low customization requirements. Main steps:

  • Create scan code option parameters;
  • Start scanning code;
  • Realize the callback interface to receive scan code results.
fun startDefaultMode(view: View) {
    // Scan code option parameter
    val options =
        HmsScanAnalyzerOptions.Creator().setHmsScanTypes(HmsScan.ALL_SCAN_TYPE).create()
    ScanUtil.startScan(
        this, REQUEST_CODE_SCAN_DEFAULT_MODE,
        options
    )
}

override fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent?). {
    super.onActivityResult(requestCode, resultCode, data)
    if(resultCode ! = RESULT_OK ||data= =null) {
        return
    }
    when (requestCode) {
        REQUEST_CODE_SCAN_DEFAULT_MODE -> {
            val hmsScan: HmsScan? = data.getParcelableExtra(ScanUtil.RESULT) // Obtain the scan code RESULT scanutil. RESULT
            if (!TextUtils.isEmpty(hmsScan?.getOriginalValue())) {
                mBinding.tvResult.text = hmsScan?.getOriginalValue()
            }
        }
		……
    }
}
Copy the code

Customized View Mode

Customize View Allows developers to customize the Scan page, and Scan Kit controls the Scan process and cameras. This mode is more suitable for scenarios that require customization of the Scan code interface. The Scan code capability of The Scan Kit is used to fulfill the CUSTOMIZATION requirements of the UI. Main steps:

  • Custom scan code page elements;
  • Use Customized View to achieve the camera scanning function. [For details, see the note].
class CustomizedModeActivity : AppCompatActivity() {
    companion object {
        const val SCAN_RESULT = "scanResult"
        private const val SCAN_FRAME_SIZE = 300
    }

    private var remoteView: RemoteView? = null
    var mScreenWidth = 0
    var mScreenHeight = 0

    private val mBinding by lazy {
        ActivityCustomizedModeBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(mBinding.root)

        //1. Get the screen density to calculate the ViewFinder's rectangle
        val dm = resources.displayMetrics
        //2. Get the screen size
        val density = dm.density
        mScreenWidth = dm.widthPixels
        mScreenHeight = dm.heightPixels
        val scanFrameSize = (SCAN_FRAME_SIZE * density)
        //3. Calculate the viewinder rectangle and place it in the center of the layout
        val rect = Rect()
        apply {
            rect.left = (mScreenWidth / 2 - scanFrameSize / 2).toInt()
            rect.right = (mScreenWidth / 2 + scanFrameSize / 2).toInt()
            rect.top = (mScreenHeight / 2 - scanFrameSize / 2).toInt()
            rect.bottom = (mScreenHeight / 2 + scanFrameSize / 2).toInt()
        }
        // 4. Initialize RemoteView and set callback listener, where scan option may be set
        remoteView = RemoteView.Builder().setContext(this).setBoundingBox(rect) .setFormat(HmsScan.ALL_SCAN_TYPE).build() remoteView? .onCreate(savedInstanceState) remoteView? .setOnResultCallback { result ->if(result ! =null && result.isNotEmpty() && result[0] != null && !TextUtils.isEmpty(
                    result[0].getOriginalValue()
                )
            ) {
                val intent = Intent()
                intent.apply {
                    putExtra(SCAN_RESULT, result[0])
                }
                setResult(Activity.RESULT_OK, intent)
                this.finish()
            }
        }
        // 5. Add RemoteView to layout.
        val params = FrameLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.MATCH_PARENT
        )
        mBinding.rim1.addView(remoteView, params)
    }

    // 6. Manage the life cycle of RemoteView
    override fun onStart(a) {
        super.onStart() remoteView? .onStart() }override fun onResume(a) {
        super.onResume() remoteView? .onResume() }override fun onPause(a) {
        super.onPause() remoteView? .onPause() }override fun onDestroy(a) {
        super.onDestroy() remoteView? .onDestroy() }override fun onStop(a) {
        super.onStop() remoteView? .onStop() } }Copy the code

Bitmap Mode

As the name suggests, this pattern identifies objects as bitmaps regardless of where the Bitmap came from. In the first two modes, we only need the relational UI interface and scan code option, while the Bitmap mode is more flexible. You can generate a Bitmap for detection by taking frames from the camera, or convert image files into bitmaps for detection, etc. Suitable for image recognition and other scenarios. Main steps:

  • Turn data into Bitmap (camera data, files, etc.)
  • Initialize theHmsScanAnalyzerOptions, set the code standard that supports identification and set the Bitmap mode to picture scanning mode;
  • callScanUtilStatic method ofdecodeWithBitmapInitiates a scan request and obtains the scan result objectHmsScan
fun startBitmapMode(view: View) {
    EasyPhotos.createAlbum(
        this.false.false,
        GlideEngine.getInstance()
    )
        .setFileProviderAuthority(BuildConfig.APPLICATION_ID)
        .setCount(1)
        .start(object : SelectCallback() {
            override fun onResult(photos: ArrayList<Photo>? , isOriginal:Boolean){ photos? .let {val path = photos.first().path
                    if (TextUtils.isEmpty(path)) {
                        return
                    }
                    // 1. Convert to Bitmap
                    val bitmap = ScanUtil.compressBitmap(this@MainActivity, path)
                    // 2. Call decodeWithBitmap to identify Bitmap.
                    val result = ScanUtil.decodeWithBitmap(
                        this@MainActivity,
                        bitmap,
                        HmsScanAnalyzerOptions.Creator().setHmsScanTypes(0).setPhotoMode(false)
                            .create()
                    )
                    // 3. Display the identification result
                    if(result ! =null && result.isNotEmpty()) {
                        if(! TextUtils.isEmpty(result[0].getOriginalValue())) {
                            mBinding.tvResult.text = result[0].getOriginalValue()
                        }
                    }
                }
            }

            override fun onCancel(a) {
                Toast.makeText(
                    this@MainActivity."Image selection failed",
                    Toast.LENGTH_SHORT
                )
                    .show()
            }

        })
}
Copy the code

MultiProcessor Mode

Scan codes for cameras and imported images. Multiple codes can be detected simultaneously in synchronous and asynchronous modes. This Mode is a continuation of Bitmap Mode, where mlFrames are generated using bitmaps. Of course, there are other ways to generate mlFrames, but most of them are bitmaps. Main steps:

  • Turn data into Bitmap (camera data, files, etc.)

  • Initialize HmsScanAnalyzerOptions and set the code standards that support identification.

  • Initialize the HmsScanAnalyzer object.

  • The image information is converted to MLFrame, MLFrame is the image information class encapsulated by ML Kit;

  • Calls the analyseFrame scanning method of the HmsScanAnalyzer object to initiate a scanning request and obtain the scanning result.

 fun startMultiProcessorMode(view: View) {
        EasyPhotos.createAlbum(
            this.false.false,
            GlideEngine.getInstance()
        )
            .setFileProviderAuthority(BuildConfig.APPLICATION_ID)
            .setCount(1)
            .start(object : SelectCallback() {
                override fun onResult(photos: ArrayList<Photo>? , isOriginal:Boolean){ photos? .let {val path = photos.first().path
                        if (TextUtils.isEmpty(path)) {
                            return
                        }
                        // 1. Convert to Bitmap
                        val bitmap = ScanUtil.compressBitmap(this@MainActivity, path)
						// 2. Configure options
                        val options =
                            HmsScanAnalyzerOptions.Creator().setHmsScanTypes(HmsScan.ALL_SCAN_TYPE)
                                .create()
                        // 3. Initialize the HmsScanAnalyzer object
                        val scanAnalyzer = HmsScanAnalyzer(options)
                        // 4. Build the MLFrame
                        val image = MLFrame.fromBitmap(bitmap)
                        // 5. Scan code identification
                        / * synchronous mode val result: SparseArray < HmsScan > = scanAnalyzer. AnalyseFrame (image) Log. D (TAG, and the result. The toString ()) * /
                        // Asynchronous mode
                        scanAnalyzer.analyzInAsyn(image).addOnSuccessListener {
                            if(it ! =null && it.size > 0) {
                                var resultStr = ""
                                it.forEach { value ->
                                    resultStr = resultStr.plus(value.originalValue).plus("\n") } mBinding.tvResult.text = resultStr } }.addOnFailureListener { it? .printStackTrace() Log.d(TAG, it.message ? :"")}}}override fun onCancel(a) {
                    Toast.makeText(
                        this@MainActivity."Image selection failed",
                        Toast.LENGTH_SHORT
                    )
                        .show()
                }

            })
    }
Copy the code

The effect

The source code

Github.com/onlyloveyd/…