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.
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 |
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.
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:
In this code lab, you will learn how to:
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:
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.
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;
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;
}
}
}
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)
}
}
}
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()
}
}
}
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
)
}
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)
}
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
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.
dependencies {
// Import the general card recognition plug-in package.
implementation 'com.huawei.hms:ml-computer-card-gcr-plugin:2.0.1.300'
}
apply plugin: 'com.huawei.agconnect'
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() {}
}
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)
}
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)
}
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
}
Note: To maintain the user confidentiality, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document.
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.
dependencies {
// Import the bank card recognition plug-in package.
implementation 'com.huawei.hms:ml-computer-card-bcr:2.0.3.300'
}
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() {}
}
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)}
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)
}
}
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.
dependency {
implementation "com.huawei.hms:scan:1.2.0.301"
}
private Bitmap qrBitmap;
int type = HmsScan.QRCODE_SCAN_TYPE;
int width = 200;
int height = 200;
HmsBuildBitmapOption options = new HmsBuildBitmapOption.Creator()
.setBitmapMargin(0).create();
"QrCodeGeneratorMainActivity.kt".
val options = HmsBuildBitmapOption.Creator()
.setBitmapMargin(0).create()
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);
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);
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);
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.