[Huawei Developers] Basavaraj.navi:
Introduction​

If are you new to this application, please follow my previous articles

In this article, we will learn how to integrate Huawei Scan Kit in Pygmy collection finance application.

HUAWEI Scan Kit scans and parses all major 1D and 2D barcodes as well as generates barcodes to help you to quickly build barcode scanning functions into your apps. Scan Kit automatically detects, magnifies, and recognizes barcodes from a distance, and also can scan a very small barcode in the same way. It works even in suboptimal situations, such as under dim lighting or when the barcode is reflective, dirty, blurry, or printed on a cylindrical surface. This leads to a higher scanning success rate, and an improved user experience.

Scan Kit Capabilities:

13 global barcode format supported

Long range of detection

Auto Zoom

Orientation Independent

Multi-code recognition

Runs on device

Doesn’t need Internet connection

Best latency and accuracy provided

Recognition in complex scenarios as well.

There are three type of scan type.

Default View

Customized View

Multiprocessor Camera

Default View: In Default View mode, Scan Kit scans the barcodes using the camera or from images in the album. You do not need to worry about designing a UI as Scan Kit provides one.

Customized View: In Customized View mode, you do not need to worry about developing the scanning process or camera control. Scan Kit will do all these tasks for you. However, you will need to customize the scanning UI according to the customization options that Flutter Scan Plugin provides. This can also be easily completed based on the sample code below.

Multiprocessor Camera: Multiprocessor Camera Mode is used to recognize multiple barcodes simultaneously from the scanning UI or from the gallery. Scanning results will be returned as a list and during the scanning, the scanned barcodes will be caught by rectangles and their values will be shown on the scanning UI. In Multiprocessor Camera mode, you do not need to worry about developing the scanning process or camera control. Scan Kit will do all these tasks for you. However, you will need to customize the scanning UI according to the customization options that Flutter Scan Plugin provides.

In this article, we will learn Customized view in Pygmy collection application.

How to integrate Huawei Scan Kit in Android finance application?

Follow the steps.

1. Configure application on the AGC.

2. Client application development process.

Configure application on the AGC

Follow the steps.

Step 1: We need to register as a developer account in AppGallery Connect. If you are already developer ignore this step.

Step 2: Create an app by referring to Creating a Project and Creating an App in the Project

Step 3: Set the data storage location based on current location.

Step 4: Enabling Scan Kit. Project setting > Manage API > Enable Scan kit toggle button.

Step 5: Generating a Signing Certificate Fingerprint.

Step 6: Configuring the Signing Certificate Fingerprint.

Step 7: Download your agconnect-services.json file, paste it into the app root directory.

Client application development process

Follow the steps.

Step 1: Create Android application in the Android studio (Any IDE which is your favorite)

Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.

apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'

dependencies {
//Huawei Scan
implementation 'com.huawei.hms:scan:1.3.2.300'
}

Root level gradle dependencies.

maven { url 'https://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.4.1.300'

Step 3: Add storage and camera permission in AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />

Step 4: Build Application.

OnClick of QR code Icon

scanQrCode.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//requestCamera();
requestPermission(DEFINED_CODE, DECODE);
}
});

Request runtime permission.

/**
* Apply for permissions.
*/
private void requestPermission(int requestCode, int mode) {
if (mode == DECODE) {
decodePermission(requestCode);
} else if (mode == GENERATE) {
generatePermission(requestCode);
}
}

/**
* Apply for permissions.
*/
private void decodePermission(int requestCode) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE},
requestCode);
}

/**
* Apply for permissions.
*/
private void generatePermission(int requestCode) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
requestCode);
}

After permission granting it will redirect to another activity DefinedActivity

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CAMERA) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startCamera();
} else {
Toast.makeText(this, "Camera Permission Denied", Toast.LENGTH_SHORT).show();
}
}

//Customized View Mode
if (requestCode == DEFINED_CODE) {
Intent intent = new Intent(this, DefinedActivity.class);
this.startActivityForResult(intent, REQUEST_CODE_DEFINE);
}
}

DefinedActivity.java

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.huawei.hms.hmsscankit.OnLightVisibleCallBack;
import com.huawei.hms.hmsscankit.OnResultCallback;
import com.huawei.hms.hmsscankit.RemoteView;
import com.huawei.hms.hmsscankit.ScanUtil;
import com.huawei.hms.ml.scan.HmsScan;
import com.huawei.hms.ml.scan.HmsScanAnalyzerOptions;
import com.shea.pygmycollection.R;

import java.io.IOException;

public class DefinedActivity extends Activity {
private FrameLayout frameLayout;
private RemoteView remoteView;
private ImageView backBtn;
private ImageView imgBtn;
private ImageView flushBtn;
int mScreenWidth;
int mScreenHeight;
//The width and height of scan_view_finder is both 240 dp.
final int SCAN_FRAME_SIZE = 240;

private int[] img = {R.drawable.flashlight_on, R.drawable.flashlight_off};
private static final String TAG = "DefinedActivity";

//Declare the key. It is used to obtain the value returned from Scan Kit.
public static final String SCAN_RESULT = "scanResult";
public static final int REQUEST_CODE_PHOTO = 0X1113;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_defined);
// Bind the camera preview screen.
frameLayout = findViewById(R.id.rim);

//1. Obtain the screen density to calculate the viewfinder's rectangle.
DisplayMetrics dm = getResources().getDisplayMetrics();
float density = dm.density;
//2. Obtain the screen size.
mScreenWidth = getResources().getDisplayMetrics().widthPixels;
mScreenHeight = getResources().getDisplayMetrics().heightPixels;

int scanFrameSize = (int) (SCAN_FRAME_SIZE * density);

//3. Calculate the viewfinder's rectangle, which in the middle of the layout.
//Set the scanning area. (Optional. Rect can be null. If no settings are specified, it will be located in the middle of the layout.)
Rect rect = new Rect();
rect.left = mScreenWidth / 2 - scanFrameSize / 2;
rect.right = mScreenWidth / 2 + scanFrameSize / 2;
rect.top = mScreenHeight / 2 - scanFrameSize / 2;
rect.bottom = mScreenHeight / 2 + scanFrameSize / 2;

//Initialize the RemoteView instance, and set callback for the scanning result.
remoteView = new RemoteView.Builder().setContext(this).setBoundingBox(rect).setFormat(HmsScan.ALL_SCAN_TYPE).build();
// When the light is dim, this API is called back to display the flashlight switch.
flushBtn = findViewById(R.id.flush_btn);
remoteView.setOnLightVisibleCallback(new OnLightVisibleCallBack() {
@Override
public void onVisibleChanged(boolean visible) {
if(visible){
flushBtn.setVisibility(View.VISIBLE);
}
}
});
// Subscribe to the scanning result callback event.
remoteView.setOnResultCallback(new OnResultCallback() {
@Override
public void onResult(HmsScan[] result) {
//Check the result.
if (result != null && result.length > 0 && result[0] != null && !TextUtils.isEmpty(result[0].getOriginalValue())) {
Intent intent = new Intent();
intent.putExtra(SCAN_RESULT, result[0]);
setResult(RESULT_OK, intent);
DefinedActivity.this.finish();
}
}
});
// Load the customized view to the activity.
remoteView.onCreate(savedInstanceState);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
frameLayout.addView(remoteView, params);
// Set the back, photo scanning, and flashlight operations.
backBtn = findViewById(R.id.back_img);
backBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DefinedActivity.this.finish();
}
});
//setBackOperation();
setPictureScanOperation();
setFlashOperation();
}

/**
* Call the lifecycle management method of the remoteView activity.
*/
private void setPictureScanOperation() {
imgBtn = findViewById(R.id.img_btn);
imgBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent pickIntent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
DefinedActivity.this.startActivityForResult(pickIntent, REQUEST_CODE_PHOTO);

}
});
}

private void setFlashOperation() {
flushBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (remoteView.getLightStatus()) {
remoteView.switchLight();
flushBtn.setImageResource(img[1]);
} else {
remoteView.switchLight();
flushBtn.setImageResource(img[0]);
}
}
});
}

private void setBackOperation() {
backBtn = findViewById(R.id.back_img);
backBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DefinedActivity.this.finish();
}
});
}

/**
* Call the lifecycle management method of the remoteView activity.
*/
@Override
protected void onStart() {
super.onStart();
remoteView.onStart();
}

@Override
protected void onResume() {
super.onResume();
remoteView.onResume();
}

@Override
protected void onPause() {
super.onPause();
remoteView.onPause();
}

@Override
protected void onDestroy() {
super.onDestroy();
remoteView.onDestroy();
}

@Override
protected void onStop() {
super.onStop();
remoteView.onStop();
}

/**
* Handle the return results from the album.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_PHOTO) {
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData());
HmsScan[] hmsScans = ScanUtil.decodeWithBitmap(DefinedActivity.this, bitmap, new HmsScanAnalyzerOptions.Creator().setPhotoMode(true).create());
if (hmsScans != null && hmsScans.length > 0 && hmsScans[0] != null && !TextUtils.isEmpty(hmsScans[0].getOriginalValue())) {
Intent intent = new Intent();
intent.putExtra(SCAN_RESULT, hmsScans[0]);
setResult(RESULT_OK, intent);
DefinedActivity.this.finish();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

activity_defined.xml

<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:my_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<FrameLayout
android:id="@+id/rim"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center_vertical">

<ImageView
android:id="@+id/flush_btn"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:visibility="gone"
android:gravity="center"
android:src="@drawable/flashlight_off" />
</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:alpha="0.1"
android:background="#FF000000" />

<TextView
android:layout_above="@id/scan_area"
android:layout_marginBottom="10dp"
android:layout_centerHorizontal="true"
android:text="@string/scan_tip"
android:textAllCaps="false"
android:textColor="#FFFFFF"
android:textSize="15sp"
android:textStyle="bold"
android:layout_height="20dp"
android:layout_width="220dp" />

<ImageView
android:id="@+id/scan_area"
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:background="@drawable/cloors" />
</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:background="@color/colorPrimary">

<TextView
android:layout_marginStart="10sp"
android:layout_toEndOf="@+id/back_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/title"
android:textAllCaps="false"
android:textColor="#FFFFFF"
android:textSize="20sp"
android:textStyle="bold" />

<ImageView
android:id="@+id/back_img"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:layout_alignParentStart="true"
android:layout_marginStart="12dp"
android:layout_marginTop="4dp"
android:gravity="center"
android:src="@drawable/back" />

<ImageView
android:id="@+id/img_btn"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="12dp"
android:layout_marginTop="4dp"
android:gravity="center"
android:src="@drawable/photo" />
</RelativeLayout>

</FrameLayout>

OnActvityResult of first screen REQUEST_CODE_DEFINE returns set the respected account details to screen.

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
IntentResult intentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (intentResult != null) {
if (intentResult.getContents() == null) {
//textView.setText(“Cancelled”);
Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show();
} else {
//textView.setText(intentResult.getContents());
CollectionModel collectionModel = new Gson().fromJson(intentResult.getContents(), CollectionModel.class);
if (collectionModel != null && collectionModel.getIsPygmyApp().equals("1")) {
updateUi(collectionModel);
} else {
Toast.makeText(this, "Invalid QR Code", Toast.LENGTH_SHORT).show();
}
}
}

if (requestCode == REQUEST_CODE_DEFINE && data != null) {
HmsScan obj = data.getParcelableExtra(DefinedActivity.SCAN_RESULT);
if (obj != null) {
CollectionModel collectionModel = new Gson().fromJson(obj.getOriginalValue(), CollectionModel.class);
if (collectionModel != null && collectionModel.getIsPygmyApp().equals("1")) {
updateUi(collectionModel);
} else {
Toast.makeText(this, "Invalid QR Code", Toast.LENGTH_SHORT).show();
}
Log.e("data: ", new Gson().toJson(obj));
}
} else {
Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show();
}
super.onActivityResult(requestCode, resultCode, data);
}

Result​

Generating QR Code

Scanning QR Code

Tips and Tricks​

Make sure you are already registered as Huawei developer.

Make sure you have already downloaded service.agconnect.json and added it to app folder.

Make sure all the dependencies are added.

Do not forget to add the camera and storage permission.

If you are running android version 6 or later, follow the runtime permission rule.

Conclusion​

In this article, we have learnt how to integrate Scan kit in Android. We have learnt the types of scan available. And we have learnt how to use the Customized view. Collecting cash using the QR code in each shop makes agent life easy. In upcoming article I’ll come up with new article.

Reference​

Scan kit