Expert: Integrate to Generate and Scan QR Code APIs on Harmony OS

Manoj Kumar
5 min readDec 31, 2021

Overview

In this article, I will create a demo app along with the integration of Generate and Scan QR Code APIs which is based on Harmony OS. I will provide the use case of generating and scan QR code in Harmony OS based on application.

Harmony OS QR Code Introduction

Harmony OS offers APIs to generates and scan QR Code. It also provides the solution of generating a health QR code based on the body temperature data, identifying the QR code of an official account to follow the account and scanning the QR code provided by a merchant to make a payment.

API Overview

Camera Opening

Step 1 — Extend the SurfaceProvider class, call the getSurfaceOps().get().addCallback(this) function, and call getSurfaceOps().surfaceCreated() to obtain a Surface object to display the camera window. The sample code is as follows:

public class CameraPreview extends SurfaceProvider implements SurfaceOps.Callback {

private Surface previewSurface;

public CameraPreview(Context context) {

super(context);

getSurfaceOps().get().addCallback(this);

}

@Override

public void surfaceCreated(SurfaceOps surfaceOps) {

previewSurface = surfaceOps.getSurface();

}

}

Step 2 — Use the CameraKit.getInstance() method to obtain a CameraKit object and call the CameraKit.createCamera() method to create a Camera object. The sample code is as follows:

private void openCamera() {

CameraKit cameraKit = CameraKit.getInstance(getContext());

String[] cameraIds = cameraKit.getCameraIds();

cameraKit.createCamera(cameraIds[0], new CameraStateCallbackImpl()

new EventHandler(EventRunner.create(“qr”)));

}

Step 3 — Define the CameraStateCallback class. After the Camera object is created, the onCreated() method is called. Call the CameraConfig.Builder.addSurface() method to configure the camera Surface. The sample code is as follows:

private class CameraStateCallbackImpl extends CameraStateCallback {

@Override

public void onCreated(Camera camera) {

super.onCreated(camera);

CameraConfig.Builder cameraBuilder = camera.getCameraConfigBuilder();

cameraBuilder.addSurface(previewSurface);

camera.configure(cameraBuilder.build());

}

}

Data Collection and Parsing

Use the ImageReceiver class to receive each frame of data from the camera and convert each frame of data into a PixelMap object. The procedure is as follows:

Step 1 — Define the ImageReceiver.IImageArrivalListener class and call the onImageArrival() method to process each frame of data. The sample code is as follows:

private ImageReceiver.IImageArrivalListener imageArrivalListener = new ImageReceiver.IImageArrivalListener() {

@Override

public void onImageArrival(ImageReceiver imageReceiver) {

// Receive each frame of data.

}

};

Step 2 — Initialize the ImageReceiver object, with the third parameter to ImageFormat.JPEG, and set an IImageArrivalListener. The sample code is as follows:

ImageReceiver imageReceiver = ImageReceiver.create(SCREEN_HEIGHT, SCREEN_WIDTH, ImageFormat.JPEG, IMAGE_RCV_CAPACITY);

imageReceiver.setImageArrivalListener(imageArrivalListener);

Step 3 — Call the ImageReceiver.getRecevingSurface() method to obtain a Surface object and configure it in CameraConfig.Builder to receive each frame of data from the camera. The sample code is as follows:

@Override

public void onCreated(Camera camera) {

super.onCreated(camera);

CameraConfig.Builder cameraBuilder = camera.getCameraConfigBuilder();

Surface imageSurface = imageReceiver.getRecevingSurface();

cameraBuilder.addSurface(imageSurface);

}

Step 4 — Call the ImageReceiver.readNextImage() method to obtain an ohos.media.image.Image, and then call the ohos.media.image.Image.Component.read() method to read the image data to a byte array. The sample code is as follows:

@Override

public void onImageArrival(ImageReceiver imageReceiver) {

ohos.media.image.Image image = imageReceiver.readNextImage();

ohos.media.image.Image.Component component = image.getComponent(ImageFormat.ComponentType.JPEG);

byte[] data = new byte[component.remaining()];

component.read(data);

}

Step 5 — Use ImageSource to decode the byte array to obtain a PixelMap object. The sample code is as follows:

ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();

sourceOptions.formatHint = “image/jpg”;

ImageSource imageSource = ImageSource.create(data, sourceOptions);

PixelMap pixelMap = imageSource.createPixelmap(null);

Prerequisite

· Harmony OS phone.

· Java JDK.

· DevEco Studio.

App Development

· Create a New Harmony OS Project.

· Configure Project config.json.

{
"app": {
"bundleName": "com.hms.harmony",
"vendor": "huawei",
"version": {
"code": 1000000,
"name": "1.0.0"
},
"apiVersion": {
"compatible": 5,
"target": 5
}
},
"deviceConfig": {},
"module": {
"package": "com.huawei.cookbook",
"name": ".MyApplication",
"deviceType": [
"phone",
"tv",
"tablet"
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "entry",
"moduleType": "entry",
"installationFree": true
},
"reqPermissions": [
{
"name": "ohos.permission.CAMERA"
}
],
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"orientation": "portrait",
"name": "com.huawei.cookbook.MainAbility",
"icon": "$media:icon",
"description": "$string:description",
"label": "$string:app_name",
"type": "page",
"launchType": "standard"
},
{
"orientation": "portrait",
"name": "com.huawei.cookbook.ScanAbility",
"icon": "$media:icon",
"description": "$string:description",
"label": "$string:app_name",
"type": "page",
"launchType": "standard"
}
]
}
}

· Configure Project Gradle.

apply plugin: 'com.huawei.ohos.app'ohos {
compileSdkVersion 5
defaultConfig {
compatibleSdkVersion 5
}
}
buildscript {
repositories {
maven {
url 'https://repo.huaweicloud.com/repository/maven/'
}
maven {
url 'https://developer.huawei.com/repo/'
}
jcenter()
}
dependencies {
classpath 'com.huawei.ohos:hap:2.4.4.2'
classpath 'com.huawei.ohos:decctest:1.0.0.7'
}
}
allprojects {
repositories {
maven {
url 'https://repo.huaweicloud.com/repository/maven/'
}
maven {
url 'https://developer.huawei.com/repo/'
}
jcenter()
}
}

· Configure App Gradle.

apply plugin: 'com.huawei.ohos.hap'
apply plugin: 'com.huawei.ohos.decctest'
ohos {
compileSdkVersion 5
defaultConfig {
compatibleSdkVersion 5
}
buildTypes {
release {
proguardOpt {
proguardEnabled false
rulesFiles 'proguard-rules.pro'
}
}
}
signingConfigs {
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
testImplementation 'junit:junit:4.13'
ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.100'
}
decc {
supportType = ['html','xml']
}

· Create Ability class with XML UI.

MainAbilitySlice.java:

package com.huawei.cookbook.slice;import com.huawei.cookbook.ScanAbility;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import com.huawei.cookbook.ResourceTable;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Component;
public class MainAbilitySlice extends AbilitySlice { @Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
initViewEvent();
}
private void initViewEvent() {
presentSlice(ResourceTable.Id_codeGeneration,
new CodeGenerationAbilitySlice());
presentSlice(ResourceTable.Id_codeIdentification,
new CodeIdentificationAbilitySlice());
codeScanning();
}
private void codeScanning() {
findComponentById(ResourceTable.Id_codeScanning).setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName(getBundleName())
.withAbilityName(ScanAbility.class)
.build();
intent.setOperation(operation);
startAbility(intent);
}
});
}
private void presentSlice(int resId, AbilitySlice targetSlice) {
findComponentById(resId).setClickedListener(
component -> present(targetSlice, new Intent()));
}
}

MainAbility.java:

package com.huawei.cookbook;import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilitySliceAnimator;
import ohos.aafwk.content.Intent;
import ohos.bundle.IBundleManager;
import ohos.security.SystemPermission;
import com.huawei.cookbook.slice.MainAbilitySlice;
public class MainAbility extends Ability { private static final int REQUEST_CODE = 20210601;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
setAbilitySliceAnimator(new AbilitySliceAnimator().setDuration(0));
requestPermission();
}
private void requestPermission() {
if (verifySelfPermission(SystemPermission.CAMERA)
!= IBundleManager.PERMISSION_GRANTED) {
// has no permission
if (canRequestPermission(SystemPermission.CAMERA)) {
// toast
requestPermissionsFromUser(
new String[]{SystemPermission.CAMERA}, REQUEST_CODE);
}
}
}
}

ability_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<Button
ohos:id="$+id:codeGeneration"
ohos:height="match_content"
ohos:width="match_parent"
ohos:background_element="$graphic:background_button"
ohos:bottom_padding="10vp"
ohos:layout_alignment="horizontal_center"
ohos:left_margin="60vp"
ohos:left_padding="16vp"
ohos:right_margin="60vp"
ohos:right_padding="16vp"
ohos:text="Code generation"
ohos:text_color="#ffffff"
ohos:text_size="30"
ohos:top_margin="140vp"
ohos:top_padding="10vp"
/>
<Button
ohos:id="$+id:codeIdentification"
ohos:height="match_content"
ohos:width="match_parent"
ohos:background_element="$graphic:background_button"
ohos:bottom_padding="10vp"
ohos:layout_alignment="horizontal_center"
ohos:left_margin="60vp"
ohos:left_padding="16vp"
ohos:right_margin="60vp"
ohos:right_padding="16vp"
ohos:text="Code recognition"
ohos:text_color="#ffffff"
ohos:text_size="30"
ohos:top_margin="20vp"
ohos:top_padding="10vp"
/>
<Button
ohos:id="$+id:codeScanning"
ohos:height="match_content"
ohos:width="match_parent"
ohos:background_element="$graphic:background_button"
ohos:bottom_padding="10vp"
ohos:layout_alignment="horizontal_center"
ohos:left_margin="60vp"
ohos:left_padding="16vp"
ohos:right_margin="60vp"
ohos:right_padding="16vp"
ohos:text="Code scanning"
ohos:text_color="#ffffff"
ohos:text_size="30"
ohos:top_margin="20vp"
ohos:top_padding="10vp"
/>
</DirectionalLayout>

App Build Result

Tips and Tricks

· Camera permission is required. If an app does not have the camera permission, it requests the camera permission from the system before entering the scanning screen. Then, the system displays a dialog box of the permission.

· If users choose DENY or DENY AND DON’T ASK AGAIN, the current screen remains and the scanning screen cannot be displayed.

· The height ratio of the top, scanning and bottom areas can be adjusted to 2:3:3 based on the content.

Conclusion

In this article, we have learned how to implement QR Code Scan and Generate in Harmony OS application. In this application, I have explained that how users can generate and scan QR Code.

Thanks for reading this article. Be sure to like and comments to this article, if you found it helpful. It means a lot to me.

References

Harmony OS Doc:

https://developer.harmonyos.com/en/docs/design/des-guides/scan-0000001110493098

--

--