ML Kit, Scan Kit and Fido kit are most widely used in Business & Office category apps. An app can use ML kit Card Recognition feature to recognize bank cards, general visiting cards etc... Scan kit is most widely used to generate QR code or to scan the QR code.Fido kit is used for user authentication using Fingerprint or FaceRecognition.

Service Scenario Description

CardReader App will give insight about how HMS ML kit, Scan kit, and Fido kit can be used for Business&Office purpose category and how we can make the card scanning easy. This app demonstrate on how end user can scan the Bank Cards, General Cards(Business, PAN Card and Adhaar Card) and save it in the app. Whenver required User can open the app and see the stored card details. As a Safety purpose the Details of the card can only be seen after authentication using PIN Lock or FingerPrint or 3D-FaceRecognition.
In this code lab we are implementing below features with HMS kits.

Features

HMS Kits

Bank Card recognition

ML Kit

General Card recognition

ML Kit

Convert the card details to QR Code

Scan Kit

Authenticate using Fingerprint/FaceRecognition

Fido

What You Will Create

In this code lab, you will create a CardReader Demo project and use the APIs of HUAWEI ML, Scan and Fido. We are going to create a end to end prototype for scanning the different type of cards (General, Bank cards) and store it in the app.

  1. The Below are the screenshots of the CardReader Application ,the application opens with first Splash Screen the user has to agree the Terms and Conditions for Agreement page
  2. The below is the screenshort of the Dashboard Module of General ID cards and Bank Cards
  3. The below is the screenshort of the General ID Cards and Aadhar Card Results

  4. The below is the screenshort of the List of saved Cards
    List of Cards-> Displays list of saved cards.
    User Authentication-> If User want to view saved cards we have to authenticate using Pinlock or fingerprint or face-recognition for security reason.

  5. Once user has done Authentication ,user can able to view and share using QR code to share as shown in below screenshorts

Note: To maintain the user confidentiality, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document

Process:

What You Will Learn

In this code lab, you will learn how to:

Hardware Requirements

Software Requirements

Use the below link for preparing for the integration

https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0

Enable HUAWEI Service(s) in AGC console:
To integrate HMS Core kits, you must complete the following preparations:

For details, please refer to Preparations for Integrating HUAWEI HMS Core.
Enable the API permission for below kits from Project Setting > Manage APIs and enable the API permission.

Integration of HMS FIDO Kit

Fido BioAuthentication service provides local fingerprint and 3D facial authentication capabilities. Using BioAuthentication services, we can confirm whether the current user of a device is the device owner.

Step 1 : Enable the FIDO service in AGC as explained in previous steps.

Add the following dependancies

implementation'com.huawei.hms:fido-bioauthn-androidx:<FIDO_KIT_VERSION>'

Import required classes for FIDO Kit

import com.huawei.hms.support.api.fido.bioauthn.BioAuthnManager; import com.huawei.hms.support.api.fido.bioauthn.FaceManager;

Step 2 : Check Device Configuration.

If device is already configured for PIN LOCK or FingerPrint or Face Recognition, display the Agreement page to user saying that, the current Application uses Authentications services.

In JAVA
AgreementPageView.java

private void init() { BioAuthUtils.checkAuthenticationType(this); … }

BioAuthUtils.java

public static void checkAuthenticationType(Context context) { isKeyguardSecure = checkIsKeyguardSecured(context); isFingerPrintSecure = checkIsFingerprintSecured(context); isFaceRecognitionSecure = checkIsFaceRecognitionSecured(context); } public static boolean checkIsKeyguardSecured(Context context) { try { KeyguardManager mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); if (mKeyguardManager.isKeyguardSecure()) { isDeviceAuthenticated = true; return true; } return false; } catch (Exception e) { return false; } } public static boolean checkIsFingerprintSecured(Context context) { try { BioAuthnManager bioAuthnManager = new BioAuthnManager(context); int errorCode = bioAuthnManager.canAuth(); if (errorCode != 0) { Toast.makeText(context, context.getString(R.string.FP_AUTH_CHECK) + errorCode, Toast.LENGTH_LONG).show(); return false; } isDeviceAuthenticated = true; return true; } catch (Exception e) { Toast.makeText(context, R.string.FP_DEVICE_SUPPORT_ERROR, Toast.LENGTH_LONG).show(); return false; } } public static boolean checkIsFaceRecognitionSecured(Context context) { int errorCode; try { FaceManager faceManager = new FaceManager(context); errorCode = faceManager.canAuth(); } catch (Exception e) { Toast.makeText(context, R.string.FACEREC_SUPPORT_ERROR, Toast.LENGTH_LONG).show(); return false; } if (errorCode != 0) { Toast.makeText(context, context.getString(R.string.FACEREC_AUTH_CHECK) + errorCode, Toast.LENGTH_LONG).show(); return false; } isDeviceAuthenticated = true; return true; }

In Kotlin
AgreementPageView.kt

private void init() { BioAuthUtils.checkAuthenticationType(this); … }

BioAuthUtils.kt

public class BioAuthUtils { public static void checkAuthenticationType(Context context) { isKeyguardSecure = checkIsKeyguardSecured(context); isFingerPrintSecure = checkIsFingerprintSecured(context); isFaceRecognitionSecure = checkIsFaceRecognitionSecured(context); } public static boolean checkIsFaceRecognitionSecured(Context context) { int errorCode; try { FaceManager faceManager = new FaceManager(context); errorCode = faceManager.canAuth(); } catch (NullPointerException e) { Toast.makeText(context, R.string.FACEREC_SUPPORT_ERROR, Toast.LENGTH_LONG).show(); return false; } if (errorCode != 0) { Toast.makeText(context, context.getString(R.string.FACEREC_AUTH_CHECK) + errorCode, Toast.LENGTH_LONG) .show(); return false; } setIsDeviceAuthenticated(true); return true; } public static boolean checkIsFingerprintSecured(Context context) { try { BioAuthnManager bioAuthnManager = new BioAuthnManager(context); int errorCode = bioAuthnManager.canAuth(); if (errorCode != 0) { Toast.makeText(context, context.getString(R.string.FP_AUTH_CHECK) + errorCode, Toast.LENGTH_LONG) .show(); return false; } setIsDeviceAuthenticated(true); return true; } catch (NullPointerException e) { Toast.makeText(context, R.string.FP_DEVICE_SUPPORT_ERROR, Toast.LENGTH_LONG).show(); return false; } } public static boolean checkIsKeyguardSecured(Context context) { try { KeyguardManager mKeyguardManager = (KeyguardManager) context.getSystemService (Context.KEYGUARD_SERVICE); if (mKeyguardManager.isKeyguardSecure()) { setIsDeviceAuthenticated(true); return true; } else { showToast(context, context.getString(R.string.PINLOCK_SUGGESTION_MSG)); } return false; } catch (NullPointerException e) { return false; } } }

Step 3: If user doesn't click on Agree, show message to user to click on agree button.

If User Click agree, then proceed to the dashboard screen.

In JAVA
AgreementPageView.java

private void init() { BioAuthUtils.checkAuthenticationType(this); FloatingActionButton fab = findViewById(R.id.fab); fab.setOnClickListener(view -> { if (isAgreeChecked && BioAuthUtils.isDeviceAuthenticated()) { Intent i = new Intent(AgreementPageView.this, DashboardActivity.class ); startActivity(i); finish(); } else if (!isAgreeChecked) { BioAuthUtils.showGuide(view, R.string.AGREE_TO_PROCEED); } else { BioAuthUtils.showGuide(view, R.string.AUTH_SUGGESTION_MSG ); } }); }

In Kotlin
AgreementPageView.kt

private fun init() { checkAuthenticationType(this) val fab = findViewById(R.id.fab) fab.setOnClickListener { view: View? -> if (isAgreeChecked && isDeviceAuthenticated) { val i = Intent(this@AgreementPageView, DashboardActivity::class.java) startActivity(i) finish() } else if (!isAgreeChecked) { showGuide(view, R.string.AGREE_TO_PROCEED) } else { showGuide(view, R.string.AUTH_SUGGESTION_MSG) } } }

Step 4: Implementation of PINLOCK Authentication.

Invoke the PinLock Authentication Prompt to user:

In JAVA
AuthenticationPage.java

private void CheckAuthWithDeviceCredentials() { BioAuthUtils.checkAuthenticationType(this); if (BioAuthUtils.isKeyguardSecure) { KeyguardManager mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); Intent i = mKeyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.PIN_LOCK_TITLE), getString(R.string.PIN_LOCK_DESC)); try { startActivityForResult(i, PIN_LOCK_REQ_CODE); } catch (Exception e) { Log.e("PIN_LOCK_Exception", e.toString()); } } else { Toast.makeText(this, R.string.AUTH_SUGGESTION_MSG, Toast.LENGTH_LONG) .show(); } }

In KOTLIN
AuthenticationPage.kt

private fun checkAuthWithDeviceCredentials() { checkAuthenticationType(this) if (isKeyguardSecure()) { val mKeyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager val i = mKeyguardManager.createConfirmDeviceCredentialIntent( getString(R.string.PIN_LOCK_TITLE), getString(R.string.PIN_LOCK_DESC) ) try { startActivityForResult(i, PIN_LOCK_REQ_CODE) } catch (e: NullPointerException) { val intent = Intent(Settings.ACTION_SECURITY_SETTINGS) startActivityForResult( intent, SECURITY_SETTING_REQUEST_CODE ) } } else { Toast.makeText( this, R.string.AUTH_SUGGESTION_MSG,Toast.LENGTH_LONG ) .show() } }

Handling Result:
If PIN authentication is success, launch the detailed view of the user selected Card.

In JAVA
AuthenticationPage.java

@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PIN_LOCK_REQ_CODE) { if (resultCode == RESULT_OK) { Toast.makeText(this, R.string.PIN_SUCCESS, Toast.LENGTH_LONG) .show(); Intent intent = new Intent(getApplicationContext(), ProfileDetailActivity.class); intent.putExtra("id", id); intent.putExtra("qrCodeimage", qrCodeImage); startActivity(intent); finish(); } else { Toast.makeText(this, R.string.PIN_FAILURE, Toast.LENGTH_LONG) .show(); } } }

In KOTLIN
AuthenticationPage.kt

override fun onActivityResult(requestCode: Int,resultCode: Int, data:Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == PIN_LOCK_REQ_CODE) { if (resultCode == Activity.RESULT_OK) { Toast.makeText(this, R.string.PIN_SUCCESS, Toast.LENGTH_LONG).show() if (Constants.IS_DELETED_FROM_PROFILEDETAILS) { deleteCard(id) } else { val intent = Intent(applicationContext, ScannedCardDetailsActivity::class.java ) intent.putExtra(Constants.ID, id) intent.putExtra(Constants.QRCODEIMAGE, qrCodeImage) intent.putExtra(Constants.QRCODECARDTYPE, qrCodeCardType) startActivity(intent) finish() } } else { Toast.makeText(this, R.string.PIN_FAILURE, Toast.LENGTH_LONG) .show() } } }

Step 5: Implementation of Fingerprint Authentication using FIDO.

Create Fingerprint Authentication prompt callback.

In JAVA
AuthenticationPage.java

BioAuthnPrompt bioAuthnPrompt = createBioAuthnPrompt(); private BioAuthnPrompt createBioAuthnPrompt() { BioAuthnCallback fpResultCallback = new BioAuthnCallback() { @Override public void onAuthError(int errMsgId, @NonNull CharSequence errString) { } @Override public void onAuthSucceeded(@NonNull BioAuthnResult result) { } @Override public void onAuthFailed() { }; return new BioAuthnPrompt(this, ContextCompat.getMainExecutor(this), fpResultCallback); }

In KOTLIN
AuthenticationPage.kt

BioAuthnPrompt bioAuthnPrompt = createBioAuthnPrompt() private fun createBioAuthnPrompt(): BioAuthnPrompt { val fpResultCallback: BioAuthnCallback = object : BioAuthnCallback() { override fun onAuthError(errMsgId: Int, errString: CharSequence) { showToast(this@AuthenticationPage, getString(R.string.FP_AUTH_ERROR) + errMsgId + getString(R.string.errorMessage) + errString ) } override fun onAuthSucceeded(result: BioAuthnResult) { if (Constants.IS_DELETED_FROM_PROFILEDETAILS) { deleteCard(id) } else { val intent = Intent(applicationContext,ScannedCardDetailsActivity::class.java ) intent.putExtra(Constants.ID, id) intent.putExtra(Constants.QRCODEIMAGE, qrCodeImage) intent.putExtra(Constants.QRCODECARDTYPE, qrCodeCardType) startActivity(intent) finish() } } override fun onAuthFailed() { showToast(this@AuthenticationPage,getString(R.string.FP_AUTH_FAILED ) ) } } return BioAuthnPrompt(this, ContextCompat.getMainExecutor(this),fpResultCallback) }

Invoke the Fingerprint Authentication screen

In JAVA
AuthenticationPage.java

private void checkAuthwithFingerPrint() { BioAuthUtils.checkAuthenticationType(this); if (BioAuthUtils.isFingerPrintSecure) { BioAuthnManager bioAuthnManager = new BioAuthnManager(this); int errorCode = bioAuthnManager.canAuth(); if (errorCode != 0) { Toast.makeText(this, R.string.FP_AUTH_CHECK + errorCode, Toast.LENGTH_LONG).show(); return; } BioAuthnPrompt.PromptInfo.Builder builder = new BioAuthnPrompt.PromptInfo.Builder().setTitle(getString(R.string.FP_PROMPT_HEAD)) .setSubtitle(getString(R.string.FP_PROMPT_TITLE)) .setDescription(getString(R.string.FP_PROMPT_DESC)) .setDeviceCredentialAllowed(true); BioAuthnPrompt.PromptInfo info = builder.build(); bioAuthnPrompt.auth(info); } }

In KOTLIN
AuthenticationPage.kt

private fun checkAuthwithFingerPrint() { checkAuthenticationType(this) if (isFingerPrintSecure()) { val bioAuthnManager = BioAuthnManager(this) val errorCode = bioAuthnManager.canAuth() if (errorCode != 0) { Toast.makeText(this, R.string.FP_AUTH_CHECK + errorCode, Toast.LENGTH_LONG).show() return } val builder = BioAuthnPrompt.PromptInfo.Builder() .setTitle(getString(R.string.FP_PROMPT_HEAD)) .setSubtitle(getString(R.string.FP_PROMPT_TITLE)) .setDescription(getString(R.string.FP_PROMPT_DESC)) .setDeviceCredentialAllowed(true) val info = builder.build() bioAuthnPrompt!!.auth(info) } else { Toast.makeText(this, R.string.FP_DEVICE_SUPPORT_ERROR, Toast.LENGTH_LONG).show() } }

Handle the Fingerprint Response in BioAuthnCallback.

In JAVA
AuthenticationPage.java

private BioAuthnPrompt createBioAuthnPrompt() { BioAuthnCallback fpResultCallback = new BioAuthnCallback() { @Override public void onAuthError(int errMsgId, @NonNull CharSequence errString) { BioAuthUtils.showToast(AuthenticationPage.this, getString(R.string.FP_AUTH_ERROR) + errMsgId + ",errorMessage=" + errString); } @Override public void onAuthSucceeded(@NonNull BioAuthnResult result) { Intent intent=new Intent(getApplicationContext(), ProfileDetailActivity.class); // intent.putExtra("qRcodmodel", (Parcelable) qRcodmodel); intent.putExtra("id", id); intent.putExtra("qrCodeimage", qrCodeImage); startActivity(intent); finish(); } @Override public void onAuthFailed() { BioAuthUtils.showToast(AuthenticationPage.this, getString(R.string.FP_AUTH_FAILED)); } }; return new BioAuthnPrompt(this, ContextCompat.getMainExecutor(this), fpResultCallback); }

In Kotlin
AuthenticationPage.kt

private fun createBioAuthnPrompt(): BioAuthnPrompt { val fpResultCallback: BioAuthnCallback = object : BioAuthnCallback() { override fun onAuthError( errMsgId: Int, errString: CharSequence ) { showToast( this@AuthenticationPage, getString(R.string.FP_AUTH_ERROR) + errMsgId + getString(R.string.errorMessage) + errString ) } override fun onAuthSucceeded(result: BioAuthnResult) { if (Constants.IS_DELETED_FROM_PROFILEDETAILS) { deleteCard(id) } else { val intent = Intent( applicationContext, ScannedCardDetailsActivity::class.java ) intent.putExtra(Constants.ID, id) intent.putExtra(Constants.QRCODEIMAGE, qrCodeImage) intent.putExtra(Constants.QRCODECARDTYPE, qrCodeCardType) startActivity(intent) finish() } } override fun onAuthFailed() { showToast(this@AuthenticationPage, getString(R.string.FP_AUTH_FAILED)) } } return BioAuthnPrompt( this, ContextCompat.getMainExecutor(this), fpResultCallback ) }

Step 6: Implementation of FaceRecognition using FIDO.

Check whether user granted the Camera permissions or not.
If not request the user to grant the Camera permissions. It's Mandatory to check before proceeding for the Face recognition Authentication.

In JAVA
AuthenticationPage.java

private void checkAuthWithFaceRecognition() { // check camera permission int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA); if (permissionCheck != PackageManager.PERMISSION_GRANTED) { BioAuthUtils.showToast(AuthenticationPage.this, getString(R.string.FACE_AUTH_CAMERA_PERMISSION_NOT_ENABLED)); ActivityCompat.requestPermissions(AuthenticationPage.this, new String[]{Manifest.permission.CAMERA}, 1); return; } }

In Kotlin
AuthenticationPage.kt

private fun checkAuthWithFaceRecognition() { // check camera permission val permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) if (permissionCheck != PackageManager.PERMISSION_GRANTED) { showToast( this@AuthenticationPage, getString(R.string.FACE_AUTH_CAMERA_PERMISSION_NOT_ENABLED) ) ActivityCompat.requestPermissions( this@AuthenticationPage, arrayOf(Manifest.permission.CAMERA), 1 ) return }

Create Authentication Call back

In JAVA
AuthenticationPage.java

// call back BioAuthnCallback callback = new BioAuthnCallback() { @Override public void onAuthError(int errMsgId, @NonNull CharSequence errString) { } @Override public void onAuthHelp(int helpMsgId, @NonNull CharSequence helpString) { } @Override public void onAuthSucceeded(@NonNull BioAuthnResult result) { } @Override public void onAuthFailed() { BioAuthUtils.showToast(AuthenticationPage.this, getString(R.string.FACE_AUTH_FAIL)); } };

In KOTLIN
AuthenticationPage.kt

BioAuthnCallback = object : BioAuthnCallback() { override fun onAuthError( errMsgId: Int, errString: CharSequence ) { showToast( this@AuthenticationPage, getString(R.string.FACE_AUTH_ERROR) + errMsgId + getString(R.string.errorMessage) + errString + if (errMsgId == 1012) getString(R.string.FACE_CAMERA_MAY_NOT_BE) else "" ) } override fun onAuthHelp( helpMsgId: Int, helpString: CharSequence ) { showToast( this@AuthenticationPage, getString(R.string.FACE_AUTH_HELP) + helpMsgId + getString(R.string.helpString) + helpString ) } override fun onAuthSucceeded(result: BioAuthnResult) { showToast( this@AuthenticationPage, getString(R.string.FACE_AUTH_SUCCESS) ) if (Constants.IS_DELETED_FROM_PROFILEDETAILS) { deleteCard(id) } else { val intent = Intent( applicationContext, ScannedCardDetailsActivity::class.java ) intent.putExtra(Constants.ID, id) intent.putExtra(Constants.QRCODEIMAGE, qrCodeImage) intent.putExtra(Constants.QRCODECARDTYPE, qrCodeCardType) startActivity(intent) finish() } } override fun onAuthFailed() { showToast(this@AuthenticationPage, getString(R.string.FACE_AUTH_FAIL)) } }

Invoke the FaceRecognition prompt and handle the success case in callback ‘onAuthSucceeded' method.

In JAVA
AuthenticationPage.java

private void checkAuthWithFaceRecognition() { . . CancellationSignal cancellationSignal = new CancellationSignal(); FaceManager faceManager = new FaceManager(this); // Checks whether 3D facial authentication can be used. int errorCode = faceManager.canAuth(); if (errorCode != 0) { BioAuthUtils.showToast(this, "Can not authenticate. errorCode=" + errorCode); return; } int flags = 0; Handler handler = null; faceManager.auth(null, cancellationSignal, flags, callback, handler); }

In Kotlin
AuthenticationPage.kt

private void checkAuthWithFaceRecognition() { val cancellationSignal = CancellationSignal() val faceManager = FaceManager(this) val errorCode = faceManager.canAuth() if (errorCode != 0) { showToast( this, getString(R.string.FACE_CANNOT_AUTH) + errorCode ) return } val flags = 0 faceManager.auth(null, cancellationSignal, flags, callback, null) }

FIDO Kit Test Result:

Agreement Page: Authentication page:

PIN Lock Screen: Fingerprint Screen:

Note: To maintain the user confidentiality, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document

Integration of ML Kit

General Card Recognition:

The general card recognition service provides a universal development framework based on the text recognition technology. It allows you to customize the post-processing logic to extract required information from any fixed-format cards, such as Aadhar Card, Pan Card and Business card. Currently, it provides three types of APIs for general card recognition from camera streams, picture taking, and local images.

Business Card:

  1. Add the following dependencies for Genaral Card recognition
    dependencies { // Import the general card recognition plug-in package. implementation 'com.huawei.hms:ml-computer-card-gcr-plugin:2.0.1.300' }
  2. After integrating the SDK in either mode, add the following information under apply plugin: ‘com.android.application' in the file header of build.gradle(app):
    apply plugin: 'com.huawei.agconnect'

Callback Method

  1. Call the general card recognition plug-in API to obtain the recognition result through the callback function, regardless of recognition from camera streams, picture taking, or images. For details, please refer to the sample code:

    Callback Method in Java

    private MLGcrCapture.Callback callback = new MLGcrCapture.Callback() { // This method requires the following status codes: // MLGcrCaptureResult.CAPTURE_CONTINUE: The returned result does not meet the requirements (for example, no result is returned or the returned result is incorrect). In camera stream or picture taking mode, the recognition continues. // MLGcrCaptureResult.CAPTURE_STOP: The returned result meets the requirements and the recognition stops. @Override public int onResult(MLGcrCaptureResult result,Object o){ // Process the recognition result. Implement post-processing logic based on your use case to extract valid information and return the status code. if (result != null) {// Check whether a result is returned. // Recognition result processing logic. if (!isMatch(result)) {// Check whether the processing result meets the requirements. Implement the isMatch method based on your use case. return MLGcrCaptureResult.CAPTURE_CONTINUE;// The processing result does not meet the requirements, and the recognition continues. } // Process the results that meet the requirements. } return MLGcrCaptureResult.CAPTURE_STOP;// The processing ends, and the recognition exits. } @Override public void onCanceled(){ // Processing for recognition request cancelation. } // Callback method used when no text is recognized or a system exception occurs during recognition. // retCode: result code. // bitmap: bank card image that fails to be recognized. @Override public void onFailure(int retCode, Bitmap bitmap){ // Exception handling. } @Override public void onDenied(){ // Processing for recognition request deny scenarios, for example, the camera is unavailable. } };

    Callback Method in Kotlin

    private val callback: MLGcrCapture.Callback = object : MLGcrCapture.Callback { override fun onResult(result: MLGcrCaptureResult, o: Any): Int { if (result == null) { return MLGcrCaptureResult.CAPTURE_CONTINUE } if (cardTypeEnum == CardType.BusinessCard) { dataMap = GenericProcessing().processText(result.text, applicationContext) } else if (cardTypeEnum == CardType.PanCard) { dataMap = PanProcessing().processText(result.text, applicationContext) } else if (cardTypeEnum == CardType.AadharCard) { dataMap = AadhaarProcessing().processExtractedTextForFrontPic( result.text,applicationContext) } // If the results don't match return MLGcrCaptureResult.CAPTURE_STOP } override fun onCanceled() {} override fun onFailure(retCode: Int, bitmap: Bitmap) {} override fun onDenied() {} }

capture API call

Set the recognition parameters for calling the capture API of the recognizer. The recognition result is returned through the callback function created in step 1. The recognition based on picture taking and image upload.

In Java

private void startTakePhotoActivity(Object object, MLGcrCapture.Callback callback) { // Create a general card recognition service configurator. MLGcrCaptureConfig cardConfig = new MLGcrCaptureConfig.Factory().create(); // Create a general card recognition UI configurator. MLGcrCaptureUIConfig uiConfig = new MLGcrCaptureUIConfig.Factory() // Set the color of the scanning box. .setScanBoxCornerColor(Color.BLUE) // Set the prompt text in the scanning box. It is recommended that the text contain less than 30 characters. .setTipText("Taking picture, align edges") // Set the recognition screen display orientation. // MLGcrCaptureUIConfig.ORIENTATION_AUTO: adaptive mode. The display orientation is determined by the physical sensor. // MLGcrCaptureUIConfig.ORIENTATION_LANDSCAPE: landscape mode. // MLGcrCaptureUIConfig.ORIENTATION_PORTRAIT: portrait mode. .setOrientation(MLGcrCaptureUIConfig.ORIENTATION_AUTO) .create(); // Method 1: Create a general card recognition processor based on the customized card recognition UI configurator. MLGcrCapture ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture(cardConfig, uiConfig); // Method 2: Use the default UI to create a general card recognition processor. MLGcrCapture ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture(cardConfig); // Bind the general card recognition processor to the processing result callback function. ocrManager.capturePhoto(this, object, callback); }

In Kotlin

//Create a general card recognition service configurator. private fun detectPhoto(`object`: Any, callback: MLGcrCapture.Callback) { val cardConfig = MLGcrCaptureConfig.Factory().setLanguage(getString(R.string.en)).create() // Create a general card recognition UI configurator. val uiConfig = MLGcrCaptureUIConfig.Factory() .setTipText(resources.getString(R.string.capture_tip)) // Set the recognition screen display orientation. // MLGcrCaptureUIConfig.ORIENTATION_AUTO: adaptive mode. The display orientation is determined by the physical sensor. // MLGcrCaptureUIConfig.ORIENTATION_LANDSCAPE: landscape mode. // MLGcrCaptureUIConfig.ORIENTATION_PORTRAIT: portrait mode. .setOrientation(MLGcrCaptureUIConfig.ORIENTATION_AUTO).create() val ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture( cardConfig, uiConfig ) ocrManager.capturePhoto(this, `object`, callback) }

Image Based

In Java:

private void startLocalImageActivity(Bitmap bitmap, Object object, MLGcrCapture.Callback callback) { // Create a general card recognition service configurator. MLGcrCaptureConfig config = new MLGcrCaptureConfig.Factory().create(); MLGcrCapture ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture(config); // The bitmap indicates image data in bitmap format. JPG, JPEG, PNG, and BMP images are supported. ocrManager.captureImage(bitmap, object, callback); }

In Kotlin:

private fun detectLocalImage(bitmap: Bitmap?, callback: MLGcrCapture.Callback) { // Create a general card recognition service configurator. val config = MLGcrCaptureConfig.Factory().create() val ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture(config) // The bitmap indicates image data in bitmap format. JPG, JPEG, PNG, and BMP images are supported. ocrManager.captureImage(bitmap, null, callback) }

GeneralCardActivity

This Activity contains the logic of how we implemented previous steps in CardReader Application
In Java:

public class GeneralCardActivity extends AppCompatActivity implements View.OnClickListener { RadioGroup.OnCheckedChangeListener radioListener = (group, checkedId) -> { switch (checkedId) { case R.id.businesscard: updateCardType(com.example.cardreader.view.generalcard.GeneralCardActivity.CardType.BusinessCard); cardTypetoSave = "BusinessCard"; emptyimg.setVisibility(View.VISIBLE); break; case R.id.panard: updateCardType(com.example.cardreader.view.generalcard.GeneralCardActivity.CardType.PanCard); cardTypetoSave = "PanCard"; emptyimg.setVisibility(View.VISIBLE); break; case R.id.aadharcard: updateCardType(com.example.cardreader.view.generalcard.GeneralCardActivity.CardType.AadharCard); cardTypetoSave = "AadharCard"; emptyimg.setVisibility(View.VISIBLE); break; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_generalcard); getSupportActionBar().setDisplayHomeAsUpEnabled(true); initComponent(); } private void updateCardType(com.example.cardreader.view.generalcard.GeneralCardActivity.CardType type) { if (cardTypeEnum != type) { // showResult.setText(""); cardview.setVisibility(View.GONE); showFrontDeleteImage(); } cardTypeEnum = type; } void initComponent() { initial the variables here } @Override public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.avatar_add: showChoosePicDialog(); break; case R.id.avatar_delete: showFrontDeleteImage(); break; case R.id.back: finish(); break; default: break; } } private void showChoosePicDialog() { AddPictureDialog addPictureDialog = new AddPictureDialog(this, AddPictureDialog.TYPE_CUSTOM); addPictureDialog.setClickListener(new AddPictureDialog.ClickListener() { @Override public void takePicture() { detectPhoto(object, callback); } @Override public void selectImage() { startChooseImageIntentForResult(); } @Override public void doExtend() { detectPreview(object, callback); } }); addPictureDialog.show(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); Log.i(TAG, "onActivityResult requestCode " + requestCode + ", resultCode " + resultCode); if (requestCode == REQUEST_IMAGE_SELECT_FROM_ALBUM && resultCode == RESULT_OK) { if (intent != null) { mImageUri = intent.getData(); // bitmap = (Bitmap) intent.getExtras().get("data"); } tryReloadAndDetectInImage(); } else if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { tryReloadAndDetectInImage(); } } // take a picture private void detectPhoto(Object object, MLGcrCapture.Callback callback) { MLGcrCaptureConfig cardConfig = new MLGcrCaptureConfig.Factory().setLanguage("en").create(); MLGcrCaptureUIConfig uiConfig = new MLGcrCaptureUIConfig.Factory() .setTipText(getResources().getString(R.string.capture_tip)) .setOrientation(MLGcrCaptureUIConfig.ORIENTATION_AUTO).create(); MLGcrCapture ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture(cardConfig, uiConfig); ocrManager.capturePhoto(this, object, callback); } // local image private void detectLocalImage(Bitmap bitmap, Object object, MLGcrCapture.Callback callback) { MLGcrCaptureConfig config = new MLGcrCaptureConfig.Factory().create(); MLGcrCapture ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture(config); ocrManager.captureImage(bitmap, object, callback); } private MLGcrCapture.Callback callback = new MLGcrCapture.Callback() { @Override public int onResult(MLGcrCaptureResult result, Object o) { Log.i(TAG, "callback onRecSuccess"); if (result == null) { Log.e(TAG, "callback onRecSuccess result is null"); return MLGcrCaptureResult.CAPTURE_CONTINUE; } GeneralCardProcessor idCard = null; GeneralCardResult cardResult = null; if (cardTypeEnum == com.example.cardreader.view.generalcard.GeneralCardActivity.CardType.BusinessCard) { dataMap = new GenericProcessing().processText(result.text, getApplicationContext()); BusinessOutput(dataMap); } else if (cardTypeEnum == com.example.cardreader.view.generalcard.GeneralCardActivity.CardType.PanCard) { dataMap = new PanProcessing().processText(result.text, getApplicationContext()); } else if (cardTypeEnum == com.example.cardreader.view.generalcard.GeneralCardActivity.CardType.AadharCard) { dataMap = new AadhaarProcessing().processExtractedTextForFrontPic(result.text, getApplicationContext()); presentFrontOutput(dataMap); } if (idCard != null) { cardResult = idCard.getResult(); } String a = DataConverter.BitMapToString(result.cardBitmap); bitmap = DataConverter.StringToBitMap(a); //result.cardBitmap; showFrontImage(result.cardBitmap); // displayResult(cardResult); presentOutput(dataMap); // If the results don't match if (cardResult == null || cardResult.valid.isEmpty() || cardResult.number.isEmpty()) { return MLGcrCaptureResult.CAPTURE_STOP; } // displayResult(cardResult); presentOutput(dataMap); return MLGcrCaptureResult.CAPTURE_STOP; } @Override public void onCanceled() { // Processing for recognition request cancelation. } @Override public void onFailure(int retCode, Bitmap bitmap) { // Exception handling. } @Override public void onDenied() { } }; private void presentOutput(HashMap<String, String> dataMap) { if (dataMap != null) { name.setText(dataMap.get("name"), TextView.BufferType.EDITABLE); fatherName.setText(dataMap.get("fatherName"), TextView.BufferType.EDITABLE); panNumber.setText(dataMap.get("pan"), TextView.BufferType.EDITABLE); dateOfBirth.setText(dataMap.get("dob"), TextView.BufferType.EDITABLE); } } }

In Kotlin:

class GeneralCardActivity : AppCompatActivity(), View.OnClickListener { /** * The Radio listener. */ var radioListener = RadioGroup.OnCheckedChangeListener { group: RadioGroup?, checkedId: Int -> when (checkedId) { R.id.businesscard -> { updateCardType(CardType.BusinessCard)} R.id.panard -> { updateCardType(CardType.PanCard)} R.id.aadharcard -> { updateCardType(CardType.AadharCard)} } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_generalcard) Objects.requireNonNull(supportActionBar)?.setDisplayHomeAsUpEnabled(true) initComponent() } /** * updating card type. * * @param type of CardType */ private fun updateCardType(type: CardType) { if (cardTypeEnum != type) { cardview!!.visibility = View.GONE showFrontDeleteImage() } cardTypeEnum = type } /** * Init component. */ fun initComponent() { } override fun onClick(view: View) { val id = view.id when (id) { R.id.avatar_add -> detectPhoto(`object`, callback) R.id.avatar_delete -> showFrontDeleteImage() R.id.back -> finish() else -> { } } } /** * Save card. * * @param v the v */ fun saveCard(v: View?) { if (dataMap != null && !dataMap!!.isEmpty() && dataMap!!.size > 1 || contactResults != null && contactResults!!.text != null && !contactResults!!.text.toString().isEmpty() ) { save() } else { Toast.makeText(applicationContext, R.string.pleasescancard, Toast.LENGTH_LONG) .show() } } private fun save() { class SaveTask : AsyncTask() { override fun onPreExecute() { super.onPreExecute() progressDialog = ProgressDialog(this@GeneralCardActivity) progressDialog!!.setMessage(getString(R.string.saving)) progressDialog!!.isIndeterminate = false progressDialog!!.setCancelable(false) progressDialog!!.show() } protected override fun doInBackground(vararg voids: Void?): Void? { val userDetails = BusinessCardEntity() if (cardTypeToSave.equals(Constants.BUSINESS_CARD, ignoreCase = true)) { userDetails.name= DataConverter.enCodeString( mContactName!!.text.toString() ) userDetails.mobileno= DataConverter.enCodeString( mContactNumber!!.text.toString() ) userDetails.emailid= DataConverter.enCodeString( mContactEmail!!.text.toString() ) userDetails.companyname= DataConverter.enCodeString( mContactOrganization!!.text.toString() ) userDetails.address = "" userDetails.jobtitle = "" userDetails.website = "" userDetails.cardType = cardTypeToSave userDetails.image = DataConverter.bitMapToString(bitmap!!) } else if (cardTypeToSave.equals(Constants.AADHARCARD, ignoreCase = true)) { userDetails.name=DataConverter.enCodeString(name!!.text.toString()) userDetails.aadharid= DataConverter.enCodeString( aadhaarNumber!!.text.toString().trim { it <= ' ' } ) userDetails.gender=DataConverter.enCodeString(gender!!.text.toString()) userDetails.fathername= DataConverter.enCodeString( fatherName!!.text.toString() ) userDetails.dob= DataConverter.enCodeString( yearOfBirth!!.text.toString().trim { it <= ' ' }.replace (":", "") ) userDetails.address="" userDetails.cardType=cardTypeToSave userDetails.image=DataConverter.bitMapToString(bitmap!!) } else { userDetails.name=DataConverter.enCodeString(name!!.text.toString()) userDetails.pannumber= DataConverter.enCodeString( panNumber!!.text.toString() ) userDetails.fathername= DataConverter.enCodeString( fatherName!!.text.toString() ) userDetails.dob=DataConverter.enCodeString(dateOfBirth!!.text.toString()) userDetails.address="" userDetails.cardType=cardTypeToSave userDetails.image=DataConverter.bitMapToString(bitmap!!) } DatabaseClient.getInstance(applicationContext)?.appDatabase ?.businessCardEntityDao() ?.insert(userDetails) return null } override fun onPostExecute(aVoid: Void?) { super.onPostExecute(aVoid) progressDialog!!.dismiss() dataMap!!.clear() contactResults!!.text = "" frontImg!!.visibility = View.GONE frontSimpleImg!!.visibility = View.VISIBLE frontAddView!!.visibility = View.VISIBLE frontDeleteImg!!.visibility = View.GONE cardview!!.visibility = View.GONE val intent = Intent(applicationContext, ScannedCardListActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP intent.putExtra(Constants.CARD_TYPE_TO_SAVE, cardTypeToSave) startActivity(intent) } } val st = SaveTask() st.execute() } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) } override fun onActivityResult( requestCode: Int, resultCode: Int, intent: Intent? ) { super.onActivityResult(requestCode, resultCode, intent) if (requestCode == REQUEST_IMAGE_SELECT_FROM_ALBUM && resultCode == Activity.RESULT_OK) { if (intent != null) { mImageUri = intent.data } tryReloadAndDetectInImage() } else if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) { tryReloadAndDetectInImage() } } // take a picture private fun detectPhoto(`object`: Any, callback: MLGcrCapture.Callback) { val cardConfig = MLGcrCaptureConfig.Factory().setLanguage(getString(R.string.en)).create() // Create a general card recognition UI configurator. val uiConfig = MLGcrCaptureUIConfig.Factory() .setTipText(resources.getString(R.string.capture_tip)) .setOrientation(MLGcrCaptureUIConfig.ORIENTATION_AUTO).create() val ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture( cardConfig, uiConfig ) ocrManager.capturePhoto(this, `object`, callback) } // local image private fun detectLocalImage(bitmap: Bitmap?, callback: MLGcrCapture.Callback) { val config = MLGcrCaptureConfig.Factory().create() val ocrManager = MLGcrCaptureFactory.getInstance().getGcrCapture(config) ocrManager.captureImage(bitmap, null, callback) } /* Callback Function*/ private val callback: MLGcrCapture.Callback = object : MLGcrCapture.Callback { override fun onResult(result: MLGcrCaptureResult, o: Any): Int { if (result == null) { return MLGcrCaptureResult.CAPTURE_CONTINUE } if (cardTypeEnum == CardType.BusinessCard) { dataMap = GenericProcessing().processText(result.text, applicationContext) BusinessOutput(dataMap) businessCardList!!.visibility = View.VISIBLE aadharListResults!!.visibility = View.GONE panCard!!.visibility = View.GONE contactResults!!.visibility = View.GONE if (businessCardList!!.visibility == View.VISIBLE) { emptyImg!!.visibility = View.GONE } } else if (cardTypeEnum == CardType.PanCard) { dataMap = PanProcessing().processText(result.text, applicationContext) panCard!!.visibility = View.VISIBLE businessCardList!!.visibility = View.GONE aadharListResults!!.visibility = View.GONE if (businessCardList!!.visibility == View.GONE) { emptyImg!!.visibility = View.GONE } } else if (cardTypeEnum == CardType.AadharCard) { dataMap = AadhaarProcessing().processExtractedTextForFrontPic( result.text, applicationContext ) presentFrontOutput(dataMap) panCard!!.visibility = View.GONE businessCardList!!.visibility = View.GONE aadharListResults!!.visibility = View.VISIBLE if (businessCardList!!.visibility == View.GONE) { emptyImg!!.visibility = View.GONE } } val a = DataConverter.bitMapToString(result.cardBitmap) bitmap = DataConverter.stringToBitMap(a) showFrontImage(result.cardBitmap) presentOutput(dataMap) // If the results don't match return MLGcrCaptureResult.CAPTURE_STOP } override fun onCanceled() {} override fun onFailure(retCode: Int, bitmap: Bitmap) {} override fun onDenied() {} } /** * The enum Card type. */ enum class CardType { /** * Business card card type. */ BusinessCard, /** * Pan card card type. */ PanCard, /** * Aadhar card card type. */ AadharCard } /* Pan Card Output*/ private fun presentOutput(dataMap: HashMap?) { if (dataMap != null) { name!!.setText(dataMap[getString(R.string.panOutName)], TextView.BufferType.EDITABLE) fatherName!!.setText( dataMap[getString(R.string.panOutFatherName)], TextView.BufferType.EDITABLE ) panNumber!!.setText( dataMap[getString(R.string.panOutNumber)], TextView.BufferType.EDITABLE ) dateOfBirth!!.setText( dataMap[getString(R.string.panOutDob)], TextView.BufferType.EDITABLE ) } } /* Aadhar Card Output*/ private fun presentFrontOutput(dataMap: HashMap?) { if (dataMap != null) { aadharName!!.setText( dataMap[getString(R.string.aadharOutName)], TextView.BufferType.EDITABLE ) aadhaarNumber!!.setText( dataMap[getString(R.string.aadharOutNumber)], TextView.BufferType.EDITABLE ) gender!!.setText( dataMap[getString(R.string.aadharOutGender)], TextView.BufferType.EDITABLE ) fatherName!!.setText( dataMap[getString(R.string.aadharOutFatherName)], TextView.BufferType.EDITABLE ) dateOfBirth!!.setText( dataMap[getString(R.string.aadharOutDob)], TextView.BufferType.EDITABLE ) if (dataMap[getString(R.string.aadharOutYob)] != null) { yearOfBirth!!.setText( Objects.requireNonNull( dataMap[getString(R.string.aadharOutYob)] )?.replace(getString(R.string.aadharOutBirth), ""), TextView.BufferType.EDITABLE ) } } } /* Business Card Output*/ private fun BusinessOutput(dataMap: HashMap?) { if (dataMap != null) { contactResults!!.text = dataMap[getString(R.string.businessOutText)] mContactName!!.setText(dataMap[getString(R.string.businessOutName)]) mContactNumber!!.text = dataMap[getString(R.string.businessOutPhoneNumber)] mContactEmail!!.text = dataMap[getString(R.string.businessOutEmail)] mContactOrganization!!.text = dataMap[getString(R.string.businessOutOrganization)] } } private fun showFrontImage(bitmap: Bitmap) { frontImg!!.visibility = View.VISIBLE frontImg!!.setImageBitmap(bitmap) frontSimpleImg!!.visibility = View.GONE frontAddView!!.visibility = View.GONE frontDeleteImg!!.visibility = View.VISIBLE cardview!!.visibility = View.VISIBLE } private fun showFrontDeleteImage() { frontImg!!.visibility = View.GONE frontSimpleImg!!.visibility = View.VISIBLE frontAddView!!.visibility = View.VISIBLE frontDeleteImg!!.visibility = View.GONE cardview!!.visibility = View.GONE } /* Detect an Image*/ private fun tryReloadAndDetectInImage() { if (mImageUri == null) { return } try { mTryImageBitmap = MediaStore.Images.Media.getBitmap(contentResolver, mImageUri) } catch (error: IOException) { } detectLocalImage(mTryImageBitmap, callback) } override fun onDestroy() { super.onDestroy() if (imageBitmap != null && !imageBitmap!!.isRecycled) { imageBitmap!!.recycle() imageBitmap = null } } override fun onOptionsItemSelected(item: MenuItem): Boolean { val id = item.itemId if (id == android.R.id.home) { onBackPressed() return true } return super.onOptionsItemSelected(item) } companion object { private const val REQUEST_IMAGE_SELECT_FROM_ALBUM = 1000 private const val REQUEST_IMAGE_CAPTURE = 1001 }

Screenshots

Results after scaning Aadhar card, Business Card and Pan card.

Note: To maintain the user confidentiality, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document.

BankCard:

The bank card recognition service recognizes bank cards in camera streams within angle offset of 15 degrees and extracts key information such as card number and expiration date. This service works with the ID card recognition service to offer a host of popular functions such as identity verification and bank card number input, making user operations easier than ever.

  1. Add the following dependencies for Genaral Card recognition
    dependencies { // Import the bank card recognition plug-in package. implementation 'com.huawei.hms:ml-computer-card-bcr:2.0.3.300' }

Callback Method

Create the recognition result callback function and reload the onSuccess, onCanceled, onFailure, and onDenied methods.onSuccess indicates that the recognition is successful. MLBcrCaptureResult indicates the recognition result. onCanceled indicates that the user cancels the recognition. onFailure indicates that the recognition fails. onDenied indicates that the recognition request is denied, for example, the camera is unavailable
In Java:

private MLBcrCapture.Callback callback = new MLBcrCapture.Callback() { @Override public void onSuccess(MLBcrCaptureResult bankCardResult){ // Processing for successful recognition. } @Override public void onCanceled(){ // Processing for recognition request cancelation. } // Callback method used when no text is recognized or a system exception occurs during recognition. // retCode: result code. // bitmap: bank card image that fails to be recognized. @Override public void onFailure(int retCode, Bitmap bitmap){ // Processing logic for recognition failure. } @Override public void onDenied(){ // Processing for recognition request deny scenarios, for example, the camera is unavailable. } };

In Kotlin:

private val callback: MLGcrCapture.Callback = object : MLGcrCapture.Callback { override fun onResult(result: MLGcrCaptureResult, o: Any): Int { if (result == null) { return MLGcrCaptureResult.CAPTURE_CONTINUE } if (cardTypeEnum == CardType.BusinessCard) { dataMap = GenericProcessing().processText(result.text, applicationContext) } else if (cardTypeEnum == CardType.PanCard) { dataMap = PanProcessing().processText(result.text, applicationContext) } else if (cardTypeEnum == CardType.AadharCard) { dataMap = AadhaarProcessing().processExtractedTextForFrontPic( result.text,applicationContext) } // If the results don't match return MLGcrCaptureResult.CAPTURE_STOP } override fun onCanceled() {} override fun onFailure(retCode: Int, bitmap: Bitmap) {} override fun onDenied() {} }

CaptureFrame Api

Set the recognition parameters for calling the captureFrame API of the recognizer. The recognition result is returned through the callback function created in step 1.
In Java:

private void startCaptureActivity(MLBcrCapture.Callback callback) { MLBcrCaptureConfig config = new MLBcrCaptureConfig.Factory() // Set the expected result type of bank card recognition. // MLBcrCaptureConfig.RESULT_NUM_ONLY: Recognize only the band card number. // MLBcrCaptureConfig.RESULT_SIMPLE: Recognize only the bank card number and validity period. // MLBcrCaptureConfig.ALL_RESULT: Recognize information such as the band card number, validity period, issuing bank, card organization, and card type. .setResultType(MLBcrCaptureConfig.RESULT_SIMPLE) // Set the recognition screen display orientation. // MLBcrCaptureConfig.ORIENTATION_AUTO: adaptive mode. The display orientation is determined by the physical sensor. // MLBcrCaptureConfig.ORIENTATION_LANDSCAPE: landscape mode. // MLBcrCaptureConfig.ORIENTATION_PORTRAIT: portrait mode. .setOrientation(MLBcrCaptureConfig.ORIENTATION_AUTO) .create(); MLBcrCapture bankCapture = MLBcrCaptureFactory.getInstance().getBcrCapture(config); bankCapture.captureFrame(this, callback); }

In Kotlin:

private fun startCaptureActivity() { // Set the expected result type of bank card recognition. // MLBcrCaptureConfig.RESULT_NUM_ONLY: Recognize only the band card number. // MLBcrCaptureConfig.RESULT_SIMPLE: Recognize only the bank card number and validity period. // MLBcrCaptureConfig.ALL_RESULT: Recognize information such as the band card number, validity period, issuing bank, card organization, and card type. val config = MLBcrCaptureConfig.Factory() .setOrientation(MLBcrCaptureConfig.ORIENTATION_AUTO) .setResultType(MLBcrCaptureConfig.RESULT_ALL) .create() val bcrCapture = MLBcrCaptureFactory.getInstance().getBcrCapture(config) bcrCapture.captureFrame(this, callback)}

BankCard Activity

This Activity contains the logic of how we implemented previous steps in CardReader Application
In Java:

public class BankCard extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bank_card); initComponent(); } private void initComponent() { bankCardFrontImg = findViewById(R.id.avatar_img); bankCardFrontAddView.setOnClickListener(this); bankCardFrontDeleteImg.setOnClickListener(this); } @Override public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.avatar_add: startCaptureActivity(); break; case R.id.avatar_delete: showFrontDeleteImage(); lastFrontResult = ""; break; default: break; } } @Override protected void onDestroy() { super.onDestroy(); if (currentImage != null && !currentImage.isRecycled()) { currentImage.recycle(); currentImage = null; } } private void startCaptureActivity() { MLBcrCaptureConfig config = new MLBcrCaptureConfig.Factory() .setOrientation(MLBcrCaptureConfig.ORIENTATION_AUTO) .setResultType(MLBcrCaptureConfig.RESULT_ALL) .create(); MLBcrCapture bcrCapture = MLBcrCaptureFactory.getInstance().getBcrCapture(config); bcrCapture.captureFrame(this, this.callback); } private String formatCardResult(MLBcrCaptureResult result) { scannedresult = result; StringBuilder resultBuilder = new StringBuilder(); resultBuilder.append("Number:"); resultBuilder.append(result.getNumber()); resultBuilder.append(System.lineSeparator()); resultBuilder.append("Issuer:"); resultBuilder.append(result.getIssuer()); resultBuilder.append(System.lineSeparator()); resultBuilder.append("Expire: "); resultBuilder.append(result.getExpire()); resultBuilder.append(System.lineSeparator()); resultBuilder.append("Type: "); resultBuilder.append(result.getType()); resultBuilder.append(System.lineSeparator()); resultBuilder.append("Organization: "); resultBuilder.append(result.getOrganization()); resultBuilder.append(System.lineSeparator()); return resultBuilder.toString(); } private MLBcrCapture.Callback callback = new MLBcrCapture.Callback() { @Override public void onSuccess(MLBcrCaptureResult result) { if (result == null) { return; } bitmap = result.getOriginalBitmap(); showSuccessResult(bitmap, result); } @Override public void onCanceled() { } @Override public void onFailure(int retCode, Bitmap bitmap) { showResult.setText(" RecFailed "); } @Override public void onDenied() { } }; private void showSuccessResult(Bitmap bitmap, MLBcrCaptureResult idCardResult) { showFrontImage(bitmap); lastFrontResult = formatCardResult(idCardResult); showResult.setText(lastFrontResult); showResult.append(lastBackResult); numberImageView= findViewById(R.id.number); numberImageView.setImageBitmap(idCardResult.getNumberBitmap()); } private void showFrontImage(Bitmap bitmap) { bankCardFrontImg.setVisibility(View.VISIBLE); bankCardFrontImg.setImageBitmap(bitmap); bankCardFrontSimpleImg.setVisibility(View.GONE); bankCardFrontAddView.setVisibility(View.GONE); bankCardFrontDeleteImg.setVisibility(View.VISIBLE); save.setVisibility(View.VISIBLE); results.setVisibility(View.VISIBLE); emptyimg.setVisibility(View.GONE); } private void showFrontDeleteImage() { bankCardFrontImg.setVisibility(View.GONE); bankCardFrontSimpleImg.setVisibility(View.VISIBLE); bankCardFrontAddView.setVisibility(View.VISIBLE); bankCardFrontDeleteImg.setVisibility(View.GONE); save.setVisibility(View.GONE); results.setVisibility(View.GONE); }}

In Kotlin:

class BankCard : AppCompatActivity(), View.OnClickListener { private var bankCardFrontImg: ImageView? = null private var bankCardFrontSimpleImg: ImageView? = null private lateinit var bankCardFrontDeleteImg: ImageView private lateinit var numberImageView: ImageView private lateinit var bankCardFrontAddView: LinearLayout private var results: LinearLayout? = null private var showResult: TextView? = null private var lastFrontResult: String? = "" private var currentImage: Bitmap? = null private var scannedResult: MLBcrCaptureResult? = null private var bitmap: Bitmap? = null private var save: Button? = null private var emptyImg: RelativeLayout? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_bank_card) Objects.requireNonNull(supportActionBar)?.setDisplayHomeAsUpEnabled(true) val window = window window.setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE ) initComponent() } /** * Init component. */ private fun initComponent() { bankCardFrontAddView.setOnClickListener(this) bankCardFrontDeleteImg.setOnClickListener(this) } override fun onClick(view: View) { val id = view.id when (id) { R.id.avatar_add -> startCaptureActivity() R.id.avatar_delete -> { showFrontDeleteImage() lastFrontResult = "" } else -> { } } } override fun onDestroy() { super.onDestroy() if (currentImage != null && !currentImage!!.isRecycled) { currentImage!!.recycle() currentImage = null } } /** * Start Capture Activity. */ private fun startCaptureActivity() { val config = MLBcrCaptureConfig.Factory() .setOrientation(MLBcrCaptureConfig.ORIENTATION_AUTO) .setResultType(MLBcrCaptureConfig.RESULT_ALL) .create() val bcrCapture = MLBcrCaptureFactory.getInstance().getBcrCapture(config) bcrCapture.captureFrame(this, callback) } /** * Format Card Results. * * @param result of type MLBcrCaptureResult * @return String of type bankcard */ private fun formatCardResult(result: MLBcrCaptureResult): String { scannedResult = result return Constants.BANKCARDNUMBER.toString() + result.number + System.lineSeparator() + Constants.ISSUER + result.issuer + System.lineSeparator() + Constants.EXPIRE + result.expire + System.lineSeparator() + Constants.CARDTYPE + result.type + System.lineSeparator() + Constants.ORGANIZATION + result.organization + System.lineSeparator() } private val callback: MLBcrCapture.Callback = object : MLBcrCapture.Callback { override fun onSuccess(result: MLBcrCaptureResult) { if (result == null) { return } bitmap = result.originalBitmap showSuccessResult(bitmap, result) } override fun onCanceled() {} override fun onFailure(retCode: Int, bitmap: Bitmap) { showResult!!.text = getString(R.string.REC_FAILED) } override fun onDenied() {} } /** * Show Success Result. * * @param bitmap of type Bitmap * @param idCardResult MLBcrCaptureResult */ private fun showSuccessResult(bitmap: Bitmap?, idCardResult: MLBcrCaptureResult) { showFrontImage(bitmap) lastFrontResult = formatCardResult(idCardResult) showResult!!.text = lastFrontResult val lastBackResult = "" showResult!!.append(lastBackResult) numberImageView = findViewById(R.id.number) numberImageView.setImageBitmap(idCardResult.numberBitmap) } private fun showFrontImage(bitmap: Bitmap?) { bankCardFrontImg!!.visibility = View.VISIBLE bankCardFrontImg!!.setImageBitmap(bitmap) bankCardFrontSimpleImg!!.visibility = View.GONE bankCardFrontAddView!!.visibility = View.GONE bankCardFrontDeleteImg!!.visibility = View.VISIBLE save!!.visibility = View.VISIBLE results!!.visibility = View.VISIBLE emptyImg!!.visibility = View.GONE } private fun showFrontDeleteImage() { bankCardFrontImg!!.visibility = View.GONE bankCardFrontSimpleImg!!.visibility = View.VISIBLE bankCardFrontAddView!!.visibility = View.VISIBLE bankCardFrontDeleteImg!!.visibility = View.GONE save!!.visibility = View.GONE results!!.visibility = View.GONE } /** * Save card. * * @param v the v */ fun saveCard(v: View?) { if (lastFrontResult != null && !lastFrontResult!!.isEmpty()) { save() } else { Toast.makeText(applicationContext, R.string.pleasescancard, Toast.LENGTH_LONG) .show() } } private fun save() { @SuppressLint("StaticFieldLeak") class SaveTask : AsyncTask<Void?, Void?, Void?>() { protected override fun doInBackground(vararg voids: Void?): Void? { val userDetails = BusinessCardEntity() userDetails.name=getString(R.string.xxx) userDetails.accountnumber=DataConverter.enCodeString(scannedResult!!.number) userDetails.issuer=scannedResult!!.issuer userDetails.expirydate=DataConverter.enCodeString(scannedResult!!.expire) userDetails.banktype=scannedResult!!.type userDetails.bankorganization=DataConverter.enCodeString(scannedResult!!.organization ) userDetails.cardType=getString(R.string.cardType_bankCard) userDetails.image = DataConverter.bitMapToString(bitmap!!) DatabaseClient.getInstance(applicationContext)?.appDatabase ?.businessCardEntityDao() ?.insert(userDetails) return null } override fun onPostExecute(aVoid: Void?) { super.onPostExecute(aVoid) bankCardFrontImg!!.visibility = View.GONE bankCardFrontSimpleImg!!.visibility = View.VISIBLE bankCardFrontAddView!!.visibility = View.VISIBLE bankCardFrontDeleteImg!!.visibility = View.GONE showResult!!.visibility = View.GONE numberImageView!!.visibility = View.GONE val intent = Intent(applicationContext, ScannedCardListActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP intent.putExtra(Constants.CARD_TYPE_TO_SAVE, getString(R.string.cardType_bankCard)) startActivity(intent) } } val st = SaveTask() st.execute() } override fun onOptionsItemSelected(item: MenuItem): Boolean { val id = item.itemId if (id == android.R.id.home) { onBackPressed() return true } return super.onOptionsItemSelected(item) } }

Integration of Scan Kit (To Generate Barcode)

Scan Kit can convert character strings into 1D or 2D barcodes in 13 formats, including EAN-8, EAN-13, UPC-A, UPC-E, Coda bar, Code 39, Code 93, Code 128, ITF, QR code, Data Matrix, PDF417, and Aztec. Besides a character string, you still need to specify the format and size for generating a barcode.

  1. Use the barcode generation API of Scan Kit to generate a barcode as follows.
  2. As per our requirement we have chosen QR Code format.
  3. Add following dependency in your build.gradle file.
    dependency { implementation "com.huawei.hms:scan:1.2.0.301" }
  4. To Generate QR code for the details of scanned cards, first we have to declare necessary variable as shown below in the file "QrCodeGeneratorMainActivity.java" :
    private Bitmap qrBitmap; int type = HmsScan.QRCODE_SCAN_TYPE; int width = 200; int height = 200;
    • Bitmap - It is used to store the QR code image as bitmap.
    • Type - To obtain the barcode type.
    • Width and Height – To set the QR Code image size.

Build QR Bitmap

  1. Initialize HmsBuildBitmapOption and set optional parameters in "QrCodeGeneratorMainActivity.java".
    HmsBuildBitmapOption options = new HmsBuildBitmapOption.Creator() .setBitmapMargin(0).create();

    "QrCodeGeneratorMainActivity.kt".

    val options = HmsBuildBitmapOption.Creator() .setBitmapMargin(0).create()
  2. Call the buildBitmap API to generate a barcode.
    To build the QR Bitmap, get the data from scanned result and combine into the string builder for convert into a QR code in this file "QrCodeGeneratorMainActivity.java".

Generate Business Card QR Code bitmap

In Java:

stringBuilder = new StringBuilder(); stringBuilder.append("Name- " + userDetails.getName() + "\n"); stringBuilder.append("MobileNo- " + userDetails.getMobileno() + "\n"); stringBuilder.append("CompanyName- " + userDetails.getCompanyname() + "\n"); stringBuilder.append("EmailId- " + userDetails.getEmailid() + "\n"); stringBuilder.append("CardType- " + userDetails.getCardType() + "\n"); if (stringBuilder != null) { qrBitmap = ScanUtil.buildBitmap(stringBuilder.toString(), type, width, height, options); }

In Kotlin:

stringBuilder = new StringBuilder(); stringBuilder.append(getResources().getString(R.string.qrName)).append (DataConverter.deCodeString(userDetails.getName())).append (System.lineSeparator()) stringBuilder.append(getResources().getString(R.string.qrMobileno)).append (DataConverter.deCodeString(userDetails.getMobileno())).append (System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrCompanyName)).append (DataConverter.deCodeString(userDetails.getCompanyname())).append (System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrEmailId)).append (DataConverter.deCodeString(userDetails.getEmailid())).append (System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrCardType)).append (userDetails.getCardType()).append(System.lineSeparator()); qrBitmap = ScanUtil.buildBitmap (stringBuilder.toString(), type, width, height, options); qRcodmodel.setBusinesscardUserDetailsList(businessCardEntityList); qRcodmodel.setBitmap(qrBitmap);

Generate Aadhar Card QR Code bitmap

In Java:

stringBuilder = new StringBuilder(); stringBuilder.append("Name- " + userDetails.getName() + "\n"); stringBuilder.append("CardType- " + userDetails.getCardType() + "\n"); stringBuilder.append("AadhaarId- " + userDetails.getAadharid() + "\n"); stringBuilder.append("DOB- " + userDetails.getDob() + "\n"); if (stringBuilder != null) { qrBitmap = ScanUtil.buildBitmap(stringBuilder.toString(), type, width, height, options); }

In Kotlin:

stringBuilder = new StringBuilder(); stringBuilder.append(getResources().getString(R.string.qrName)).append (DataConverter.deCodeString(userDetails.getName())).append (System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrCardType)).append (userDetails.getCardType()).append(System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrAadhaarId)).append (DataConverter.deCodeString(userDetails.getAadharid())).append (System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrDOB)).append (DataConverter.deCodeString(userDetails.getDob())).append (System.lineSeparator()); qrBitmap = ScanUtil.buildBitmap (stringBuilder.toString(), type, width, height, options); qRcodmodel.setBusinesscardUserDetailsList(businessCardEntityList); qRcodmodel.setBitmap(qrBitmap);

Generate Pan Card QR Code bitmap

In Java:

stringBuilder = new StringBuilder(); stringBuilder.append("Name- " + userDetails.getName() + "\n"); stringBuilder.append("CardType- " + userDetails.getCardType() + "\n"); stringBuilder.append("PanNumber- " + userDetails.getPannumber() + "\n"); stringBuilder.append("DOB- " + userDetails.getDob() + "\n"); if (stringBuilder != null) { qrBitmap = ScanUtil.buildBitmap(stringBuilder.toString(), type, width, height, options); }

In Kotlin:

stringBuilder = new StringBuilder(); stringBuilder.append(getResources().getString(R.string.qrName)).append (DataConverter.deCodeString(userDetails.getName())).append (System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrCardType)).append (userDetails.getCardType()).append(System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrPanNumber)).append (DataConverter.deCodeString(userDetails.getPannumber())).append (System.lineSeparator()); stringBuilder.append(getResources().getString(R.string.qrDOB)).append (DataConverter.deCodeString(userDetails.getDob())).append (System.lineSeparator()); qrBitmap = ScanUtil.buildBitmap (stringBuilder.toString(), type, width, height, options);

Scan Kit Results:

Scenario 1:
This is the Business Card Profile detail page. From here user can see detailed scanned card information along with QR code image.
If user click the QR code image they can share to their friends with PDF format

Note: To maintain the user confidentiality, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document

Scenario 2:
This is the Aadhar Card Profile detail page. From here user can see detailed scanned card information along with QR code image.
If user click the QR code image they can share to their friends with PDF format.

Note: To maintain the user confidentiality, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document

Scenario 3:
This is the Pan Card Profile detail page. From here user can see detailed scanned card information along with QR code image.
If user click the QR code image they can share to their friends with PDF format.

Note: To maintain the user confidentiality, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document

Well done. You have successfully built a CardReader app and learned how to implement:

Refere below official documentation for more information.

Disclaimer: "This codelab is a reference to implement combination of multiple HMS kits in single project. The developer should verify and ensure the legal and security compliance of the relevant open source code".

You can download the source code here.

Code copied