This codelab describes how to enable a redirection to the emulated camera page following a successful authentication through facial recognition. The implementation depends on the biometric recognition and camera capabilities. In this codelab, you can call APIs to check whether a device supports facial recognition, perform a facial recognition, and open a camera to implement related functions.

The procedure is as follows:

This codelab helps you understand the core code only. For the complete code, refer to 10 Complete Sample Code. Before we start, let's have a look at the code structure of the entire project.

Facial Recognition Page

This page consists of the DirectionalLayout, two Button components, and two Text components. The two Button components are used to start and cancel facial recognition, respectively. The two Text components are used to display the title and the facial recognition result, respectively. The code in the resources\layout\ability_main.xml file is as follows:

<?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"> <Text ohos:id="$+id:text_helloworld1" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:background_ability_main" ohos:layout_alignment="horizontal_center" ohos:left_padding="80vp" ohos:right_padding="80vp" ohos:text="Biometric authentication" ohos:text_size="30fp" ohos:top_padding="100vp" /> <Text ohos:id="$+id:text_status" ohos:height="100vp" ohos:width="match_parent" ohos:background_element="$graphic:background_ability_main" ohos:layout_alignment="center" ohos:text_alignment="center" ohos:max_text_lines="3" ohos:multiple_lines="true" ohos:margin="5vp" ohos:text="" ohos:text_font="serif" ohos:text_size="30fp" ohos:top_padding="5vp" ohos:visibility="invisible" /> <Button ohos:id="$+id:button_start" ohos:height="60vp" ohos:width="match_parent" ohos:align_parent_bottom="true" ohos:background_element="$graphic:button_element" ohos:layout_alignment="horizontal_center" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:text="Start facial recognition" ohos:text_color="#000000" ohos:text_size="30fp" ohos:left_margin="15vp" ohos:right_margin="15vp" ohos:top_margin="200vp" /> <Button ohos:id="$+id:button_cancel" ohos:height="60vp" ohos:width="match_parent" ohos:align_parent_bottom="true" ohos:background_element="$graphic:button_element" ohos:layout_alignment="horizontal_center" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:text="Cancel facial recognition" ohos:margin="15vp" ohos:text_color="#000000" ohos:text_size="30fp" /> </DirectionalLayout>

Emulated Camera Page

This page consists of the DirectionalLayout, DependentLayout, and three Image components. The three Image components function as icons. From the left to the right, they are used to return to the previous page, take a photo, and switch cameras. The code in the resources\layout\ability_open_camera.xml file is as follows:

<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent"> <DependentLayout ohos:id="$+id:root_container" ohos:height="match_parent" ohos:width="match_parent"> <DirectionalLayout ohos:id="$+id:surface_container" ohos:height="match_parent" ohos:width="match_parent" /> <DirectionalLayout ohos:width="match_parent" ohos:height="match_content" ohos:align_parent_bottom="$+id:root_container" ohos:bottom_margin="30vp" ohos:orientation="horizontal"> <Image ohos:id="$+id:exit" ohos:height="match_content" ohos:width="match_parent" ohos:weight="1" ohos:enabled="false" ohos:layout_alignment="vertical_center" ohos:scale_mode="center" ohos:image_src="$media:ic_camera_back" /> <Image ohos:id="$+id:tack_picture_btn" ohos:height="match_content" ohos:width="match_parent" ohos:weight="1" ohos:enabled="false" ohos:layout_alignment="vertical_center" ohos:scale_mode="center" ohos:image_src="$media:ic_camera_photo" /> <Image ohos:id="$+id:switch_camera_btn" ohos:height="match_content" ohos:width="match_parent" ohos:weight="1" ohos:enabled="false" ohos:layout_alignment="vertical_center" ohos:scale_mode="center" ohos:image_src="$media:ic_camera_switch" /> </DirectionalLayout> </DependentLayout> </DirectionalLayout>

To ensure that the app runs properly, declare the following permissions in the config.json file:

"reqPermissions": [ { "name": "ohos.permission.ACCESS_BIOMETRIC" }, { "name": "ohos.permission.CAMERA" }, { "name": "ohos.permission.WRITE_USER_STORAGE" } ]

In addition, apply for the storage and camera permissions from the user in the onStart() method of OpenCamera. The sample code is as follows:

private void requestPermission() { String[] permissions = { // Storage permission SystemPermission.WRITE_USER_STORAGE, // Camera permission SystemPermission.CAMERA }; List<String> permissionFiltereds = Arrays.stream(permissions) .filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED) .collect(Collectors.toList()); if (permissionFiltereds.isEmpty()) { PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED); return; } requestPermissionsFromUser(permissionFiltereds.toArray(new String[permissionFiltereds.size()]), PERMISSION_REQUEST_CODE); }

On the facial recognition page (ability_main.xml), add the buttons for starting and canceling facial recognition. By listening for the click events of the buttons, you can implement the facial recognition functions. This section describes how to implement the functions of starting and canceling facial recognition.

Starting Facial Recognition

Before starting facial recognition, check whether the current device (phone) supports facial recognition. The sample code is as follows:

private void createStartListener() { // Prompt the user to look straight into the camera during facial recognition. getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true); try { // Obtain a BiometricAuthentication object. mBiometricAuthentication = BiometricAuthentication.getInstance(MainAbility.getMainAbility()); // Check whether the device supports facial recognition. int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability( BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true); if (hasAuth == BiometricAuthentication.BA_CHECK_SUPPORTED) { // Start a thread for facial recognition if it is supported. ThreadPoolExecutor pool = new ThreadPoolExecutor( POOL_CORE_SIZE, POOL_MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<>(QUEUE_SIZE), new ThreadPoolExecutor.DiscardOldestPolicy()); pool.submit(runnable); } else { // If facial recognition is not supported or any other issue occurs, display the issue on the page. // Do not send any display event via the EventHandler in the main thread. int retExcAuth = getRetExcAuth(hasAuth); getAndSetText(ResourceTable.Id_text_status, retExcAuth, true); } } catch (IllegalAccessException e) { LogUtils.error("createStartBtn", "IllegalAccessException when start auth"); } }
Facial recognition is time-consuming. You need to start a new thread. The sample code is as follows:
/** * Start a new thread for facial recognition to prevent other tasks from being interrupted. */ private Runnable runnable = new Runnable() { private void initHandler() { runner = EventRunner.getMainEventRunner(); if (runner == null) { return; } myEventHandle = new MyEventHandle(runner); } @Override public void run() { // Initialize myEventHandle. initHandler(); // Start facial recognition. startAuth(); } };

Start facial recognition. The sample code is as follows:

private void startAuth() { // retExcAuth: 0 indicates that the facial recognition succeeds; 1 indicates that the face image does not match any image added to the device; 2 indicates that the facial recognition has been canceled; 3 indicates that the facial recognition has timed out; 4 indicates that the camera fails to be opened; // 5 indicates that the system is busy, for example, the previous facial recognition is not complete; 6 indicates that any input parameter is incorrect; 7 indicates that facial recognition is locked because the maximum number of recognition failures has been reached; // 8 indicates that no face image has been added to the device; 100 indicates any other error. int retExcAuth = mBiometricAuthentication.execAuthenticationAction( BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true, false, null); // Send the facial recognition result to the main thread for processing. myEventHandler.sendEvent(retExcAuth); }

Send the facial recognition result to the main thread via the EventHandler for displaying the result on the facial recognition page. The sample code is as follows:

/** * Event handler */ private class MyEventHandle extends EventHandler { MyEventHandle(EventRunner runner) throws IllegalArgumentException { super(runner); } @Override protected void processEvent(InnerEvent event) { super.processEvent(event); int eventId = event.eventId; getAndSetText(ResourceTable.Id_text_status, eventId, true); } }

Canceling Facial Recognition

Click the button for canceling facial recognition. The sample code is as follows:

private void createCancelBtn() { // Create a click event. Component component = findComponentById(ResourceTable.Id_button_cancel); // Create the button for canceling facial recognition. Button cancelBtn = null; if (component != null && component instanceof Button) { cancelBtn = (Button) component; cancelBtn.setClickedListener(view -> { if (mBiometricAuthentication != null) { // Call the API for canceling facial recognition. int result = mBiometricAuthentication.cancelAuthenticationAction(); LogUtils.info("createCancelBtn:", result + ""); } }); } }

Page Redirection

Enable redirection to the emulated camera page after the facial recognition is successful. The sample code is as follows:

private void toAuthAfterPage() { Intent secondIntent = new Intent(); // Specify the bundleName and abilityName attributes of the FA to start. Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(OpenCamera.class.getName()) .build(); secondIntent.setOperation(operation); // Start the emulated camera page by calling startAbility. startAbility(secondIntent); }

The emulated camera page (ability_open_camera.xml) should provide the functions of opening the camera and switching between the front and rear cameras. This section describes how to implement these functions.

Initializing SurfaceProvider

Initialize the SurfaceProvider upon obtaining user authorization. The sample code is as follows:

private void initSurface() { surfaceProvider = new SurfaceProvider(this); DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig( ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT); surfaceProvider.setLayoutConfig(params); surfaceProvider.pinToZTop(false); // Add the SurfaceCallBack. surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack()); // Add the SurfaceProvider to the layout. Component component = findComponentById(ResourceTable.Id_surface_container); if (component instanceof ComponentContainer) { ((ComponentContainer) component).addComponent(surfaceProvider); } }

Implement SurfaceOps.Callback to enable the camera to be opened when a surface is created. The sample code is as follows:

/** * Implement SurfaceOps.Callback. */ class SurfaceCallBack implements SurfaceOps.Callback { @Override public void surfaceCreated(SurfaceOps callbackSurfaceOps) { if (callbackSurfaceOps != null) { callbackSurfaceOps.setFixedSize(SCREEN_HEIGHT, SCREEN_WIDTH); } openCamera(); } @Override public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) { } }

Opening the Camera

Set a listener for new images. The sample code is as follows:

private void openCamera() { imageReceiver = ImageReceiver.create(SCREEN_WIDTH, SCREEN_HEIGHT, ImageFormat.JPEG, IMAGE_RCV_CAPACITY); // Set a listener for new images. imageReceiver.setImageArrivalListener(this::saveImage); CameraKit cameraKit = CameraKit.getInstance(getApplicationContext()); String[] cameraLists = cameraKit.getCameraIds(); String cameraId = cameraLists.length > 1 && isCameraRear ? cameraLists[1] : cameraLists[0]; CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl(); cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler); } /** * Implement CameraStateCallback. */ class CameraStateCallbackImpl extends CameraStateCallback { CameraStateCallbackImpl() { } @Override public void onCreated(Camera camera) { // Obtain the preview surface. previewSurface = surfaceProvider.getSurfaceOps().get().getSurface(); if (previewSurface == null) { LogUtils.error(TAG, "Creating camera failed. Preview surface is null."); return; } // Wait until the preview surface is created. try { Thread.sleep(SLEEP_TIME); } catch (InterruptedException exception) { LogUtils.warn(TAG, "Waiting to be interrupted"); } CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder(); // Configure the preview surface. cameraConfigBuilder.addSurface(previewSurface); cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface()); camera.configure(cameraConfigBuilder.build()); cameraDevice = camera; enableImageGroup(); takePictureImage.setEnabled(true); } @Override public void onConfigured(Camera camera) { FrameConfig.Builder framePreviewConfigBuilder = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW); framePreviewConfigBuilder.addSurface(previewSurface); // Start looping capture. camera.triggerLoopingCapture(framePreviewConfigBuilder.build()); } private void enableImageGroup() { if (!exitImage.isEnabled()) { exitImage.setEnabled(true); takePictureImage.setEnabled(true); switchCameraImage.setEnabled(true); } } }

Switching Between Front and Rear Cameras

Enable a switch between the front and rear camera when the user clicks the button for switching cameras. The sample code is as follows:

private void switchClicked() { if (!takePictureImage.isEnabled()) { return; } takePictureImage.setEnabled(false); isCameraRear = !isCameraRear; openCamera(); }

The facial recognition FA (MainAbilitySlice) checks whether the device supports facial recognition, performs a facial recognition, displays the facial recognition result, and switches to the camera FA (OpenCameraSlice) if the facial recognition is successful. The camera FA (OpenCameraSlice) implements functions including opening the camera, taking photos, storing photos, and switching between cameras. The following figures show the effects.
Facial recognition page:

Facial recognition result:

Camera page:

Well done! You have successfully completed this codelab and learned:

  1. How to develop a HarmonyOS app that supports facial recognition.
  2. How to open the camera.
  3. How to develop an EventHandler used for inter-thread communication.

Writing the Page Layout and Style

  1. base/graphic/background_ability_main.xml
    <?xml version="1.0" encoding="UTF-8" ?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#FFFFFF"/> </shape>
  2. base/graphic/button_element.xml
    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="8vp"/> <solid ohos:color="#FF007DFE"/> </shape>
  3. base/layout/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"> <Text ohos:id="$+id:text_helloworld1" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:background_ability_main" ohos:layout_alignment="horizontal_center" ohos:left_padding="80vp" ohos:right_padding="80vp" ohos:text="Biometric authentication" ohos:text_size="30fp" ohos:top_padding="100vp" /> <Text ohos:id="$+id:text_status" ohos:height="100vp" ohos:width="match_parent" ohos:background_element="$graphic:background_ability_main" ohos:layout_alignment="center" ohos:text_alignment="center" ohos:max_text_lines="3" ohos:multiple_lines="true" ohos:margin="5vp" ohos:text="" ohos:text_font="serif" ohos:text_size="30fp" ohos:top_padding="5vp" ohos:visibility="invisible" /> <Button ohos:id="$+id:button_start" ohos:height="60vp" ohos:width="match_parent" ohos:align_parent_bottom="true" ohos:background_element="$graphic:button_element" ohos:layout_alignment="horizontal_center" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:text="Start facial recognition" ohos:text_color="#000000" ohos:text_size="30fp" ohos:left_margin="15vp" ohos:right_margin="15vp" ohos:top_margin="200vp" /> <Button ohos:id="$+id:button_cancel" ohos:height="60vp" ohos:width="match_parent" ohos:align_parent_bottom="true" ohos:background_element="$graphic:button_element" ohos:layout_alignment="horizontal_center" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:text="Cancel facial recognition" ohos:margin="15vp" ohos:text_color="#000000" ohos:text_size="30fp" /> </DirectionalLayout>
  4. base/layout/ability_open_camera.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"> <DependentLayout ohos:id="$+id:root_container" ohos:height="match_parent" ohos:width="match_parent"> <DirectionalLayout ohos:id="$+id:surface_container" ohos:height="match_parent" ohos:width="match_parent" /> <DirectionalLayout ohos:width="match_parent" ohos:height="match_content" ohos:align_parent_bottom="$+id:root_container" ohos:bottom_margin="30vp" ohos:orientation="horizontal"> <Image ohos:id="$+id:exit" ohos:height="170px" ohos:width="match_parent" ohos:weight="1" ohos:enabled="false" ohos:layout_alignment="vertical_center" ohos:scale_mode="center" ohos:image_src="$media:ic_camera_back" /> <Image ohos:id="$+id:tack_picture_btn" ohos:height="170px" ohos:width="match_parent" ohos:weight="1" ohos:enabled="false" ohos:layout_alignment="vertical_center" ohos:scale_mode="center" ohos:image_src="$media:ic_camera_photo" /> <Image ohos:id="$+id:switch_camera_btn" ohos:height="170px" ohos:width="match_parent" ohos:weight="1" ohos:enabled="false" ohos:layout_alignment="vertical_center" ohos:scale_mode="zoom_center" ohos:image_src="$media:ic_camera_switch" /> </DirectionalLayout> </DependentLayout> </DirectionalLayout>

Function Logic Code

  1. com/huawei/cookbook/slice/MainAbilitySlice.java
    package com.huawei.cookbook.slice; import com.huawei.cookbook.MainAbility; import com.huawei.cookbook.OpenCamera; import com.huawei.cookbook.ResourceTable; import com.huawei.cookbook.util.FaceAuthResult; import com.huawei.cookbook.util.LogUtils; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.content.Intent; import ohos.aafwk.content.Operation; import ohos.agp.components.Button; import ohos.agp.components.Component; import ohos.agp.components.Text; import ohos.agp.utils.Color; import ohos.biometrics.authentication.BiometricAuthentication; import ohos.eventhandler.EventHandler; import ohos.eventhandler.EventRunner; import ohos.eventhandler.InnerEvent; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * MainAbilitySlice * * @since 2021-04-12 */ public class MainAbilitySlice extends AbilitySlice { private static final int POOL_CORE_SIZE = 2; private static final int POOL_MAX_SIZE = 5; private static final int NO_FACE_RET = -1; private static final int KEEP_ALIVE_TIME = 3; private static final int QUEUE_SIZE = 6; private static final int RET_NOT_SUPPORTED = 1; private static final int RET_SAFE_LEVEL_NOT_SUPPORTED = 2; private static final int RET_NOT_LOCAL = 3; private EventRunner runner; private MyEventHandle myEventHandle; private BiometricAuthentication mBiometricAuthentication; /** * Start a new thread for facial recognition to prevent other tasks from being interrupted. */ private Runnable runnable = new Runnable() { private void initHandler() { runner = EventRunner.getMainEventRunner(); if (runner == null) { return; } myEventHandle = new MyEventHandle(runner); } @Override public void run() { // Initialize myEventHandle. initHandler(); // Start facial recognition. startAuth(); } }; /** * onStart * * @param intent intent */ @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); // Create the button for starting facial recognition and add a click event. createStartBtn(); // Create the button for canceling facial recognition and add a click event. createCancelBtn(); } /** * Create the button for canceling facial recognition. */ private void createCancelBtn() { // Create a click event. Component component = findComponentById(ResourceTable.Id_button_cancel); // Create a button. Button cancelBtn = null; if (component != null && component instanceof Button) { cancelBtn = (Button) component; cancelBtn.setClickedListener(view -> { if (mBiometricAuthentication != null) { // Call the API for canceling facial recognition. int result = mBiometricAuthentication.cancelAuthenticationAction(); LogUtils.info("createCancelBtn:", result + ""); } }); } } /** * Create a click event of the button for starting facial recognition. */ private void createStartBtn() { // Create a click event. Component component = findComponentById(ResourceTable.Id_button_start); // Create a button. Button featureBtn = null; if (component != null && component instanceof Button) { featureBtn = (Button) component; featureBtn.setClickedListener(view -> { createStartListener(); }); } } private void createStartListener() { // Prompt the user to look straight into the camera during facial recognition. getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true); try { // Obtain a BiometricAuthentication object. mBiometricAuthentication = BiometricAuthentication.getInstance(MainAbility.getMainAbility()); // Check whether the device supports facial recognition. // BiometricAuthentication.AuthType has three available values: // AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY indicates that fingerprint recognition is supported. // AUTH_TYPE_BIOMETRIC_FACE_ONLY indicates that facial recognition is supported. // AUTH_TYPE_BIOMETRIC_ALL indicates that both fingerprint and facial recognition are supported. // BiometricAuthentication.SecureLevel can be SECURE_LEVEL_S2 or SECURE_LEVEL_S3. SECURE_LEVEL_S2 is recommended for 2D facial recognition, and SECURE_LEVEL_S3 is recommended for 3D facial recognition. int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability( BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true); // hasAuth: 0 indicates that biometric authentication is supported; 1 indicates that biometric authentication is not supported; 2 indicates that the security level is not supported; 3 indicates that non-local authentication is used; 4 indicates that no face image has been added. if (hasAuth == 0) { ThreadPoolExecutor pool = new ThreadPoolExecutor( POOL_CORE_SIZE, POOL_MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<>(QUEUE_SIZE), new ThreadPoolExecutor.DiscardOldestPolicy()); pool.submit(runnable); } else { // If facial recognition is not supported or any other issue occurs, display the issue on the page. // Do not send any display event via the EventHandler in the main thread. int retExcAuth = getRetExcAuth(hasAuth); getAndSetText(ResourceTable.Id_text_status, retExcAuth, true); } } catch (IllegalAccessException e) { LogUtils.error("createStartBtn", "IllegalAccessException when start auth"); } } /** * Start facial recognition. */ private void startAuth() { // retExcAuth: 0 indicates that the facial recognition succeeds; 1 indicates that the face image does not match any image added to the device; 2 indicates that the facial recognition has been canceled; 3 indicates that the facial recognition has timed out; 4 indicates that the camera fails to be opened; // 5 indicates that the system is busy, for example, the previous facial recognition is not complete; 6 indicates that any input parameter is incorrect; 7 indicates that facial recognition is locked because the maximum number of recognition failures has been reached; // 8 indicates that no face image has been added to the device; 100 indicates any other error. int retExcAuth = mBiometricAuthentication.execAuthenticationAction( BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true, false, null); // Send the modified page to the main thread for execution. myEventHandle.sendEvent(retExcAuth); } /** * Obtains the code indicating whether authentication is supported. * * @param hasAuth Specifies whether authentication is supported. * @return Returns the code. */ private int getRetExcAuth(int hasAuth) { int retExcAuth; if (hasAuth == RET_NOT_SUPPORTED) { // 2D facial recognition is not supported. retExcAuth = FaceAuthResult.AUTH_2D_NOT_SUPPORTED; } else if (hasAuth == RET_SAFE_LEVEL_NOT_SUPPORTED) { // The security level is not supported. retExcAuth = FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED; } else if (hasAuth == RET_NOT_LOCAL) { // Non-local authentication is used. retExcAuth = FaceAuthResult.AUTH_NOT_LOCAL; } else { // No face image has been added to the device. retExcAuth = FaceAuthResult.AUTH_NO_FACE; } return retExcAuth; } /** * Obtains and sets text. * * @param textId Indicates the text ID. * @param retExcAuth Indicates the authentication return code. * @param isVisible Specifies whether to show the text. */ private void getAndSetText(int textId, int retExcAuth, boolean isVisible) { // Obtain the text. Component componentText = findComponentById(textId); if (componentText != null && componentText instanceof Text) { Text text = (Text) componentText; setTextValueAndColor(retExcAuth, text); if (isVisible) { text.setVisibility(Component.VISIBLE); } } } /** * Sets the prompt text. * * @param text Indicates the text to set. * @param textValue Indicates the text value. * @param color Indicates the text color. */ private void setTextValueAndColor(Text text, String textValue, Color color) { text.setText(textValue); text.setTextColor(color); } /** * Sets the text value and color. * * @param retExcAuth Indicates the authentication return code. * @param text Indicates the text to set. */ private void setTextValueAndColor(int retExcAuth, Text text) { switch (retExcAuth) { case FaceAuthResult.AUTH_SUCCESS: setTextValueAndColor(text, "Facial recognition succeeds.", Color.GREEN); // Redirect to another page. toAuthAfterPage(); break; case FaceAuthResult.AUTH_FAIL: setTextValueAndColor(text, "The face image does not match any image added to the device.", Color.RED); break; case FaceAuthResult.AUTH_CANCLE: setTextValueAndColor(text, "Authentication has been canceled.", Color.RED); break; case FaceAuthResult.AUTH_TIME_OUT: setTextValueAndColor(text, "Facial recognition has timed out.", Color.RED); break; case FaceAuthResult.AUTH_OPEN_CAMERA_FAIL: setTextValueAndColor(text, "Failed to open the camera.", Color.RED); break; case FaceAuthResult.AUTH_BUSY: setTextValueAndColor(text, "The system is busy, for example, the previous facial recognition is not complete.", Color.RED); break; case FaceAuthResult.AUTH_PARAM_ERROR: setTextValueAndColor(text, "An input parameter is incorrect.", Color.RED); break; case FaceAuthResult.AUTH_FACE_LOCKED: setTextValueAndColor(text, "Facial recognition is locked because the maximum number of recognition failures has been reached.", Color.RED); break; case FaceAuthResult.AUTH_NO_FACE: setTextValueAndColor(text, "No face image has been added.", Color.BLUE); break; case FaceAuthResult.AUTH_OTHER_ERROR: setTextValueAndColor(text, "Other error", Color.RED); break; case FaceAuthResult.AUTH_2D_NOT_SUPPORTED: setTextValueAndColor(text, "2D facial recognition is not supported.", Color.BLUE); break; case FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED: setTextValueAndColor(text, "The security level is not supported.", Color.BLUE); break; case FaceAuthResult.AUTH_NOT_LOCAL: setTextValueAndColor(text, "Non-local authentication is used.", Color.BLUE); break; default: setTextValueAndColor(text, "Starting facial recognition. Please look straight into the camera.", Color.BLUE); break; } } private void toAuthAfterPage() { Intent secondIntent = new Intent(); // Specify the bundleName and abilityName of the FA to start. Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(OpenCamera.class.getName()) .build(); secondIntent.setOperation(operation); // Start another page via the startAbility method of AbilitySlice. startAbility(secondIntent); } /** * Represents the event handler. * * @since 2021-04-12 */ private class MyEventHandle extends EventHandler { MyEventHandle(EventRunner runner) throws IllegalArgumentException { super(runner); } @Override protected void processEvent(InnerEvent event) { super.processEvent(event); int eventId = event.eventId; getAndSetText(ResourceTable.Id_text_status, eventId, true); } } @Override public void onStop() { mBiometricAuthentication.cancelAuthenticationAction(); BiometricAuthentication.AuthenticationTips authenticationTips = mBiometricAuthentication.getAuthenticationTips(); String tips = authenticationTips.tipInfo; } }
  2. com/huawei/cookbook/slice/OpenCameraSlice.java
    package com.huawei.cookbook.slice; import com.huawei.cookbook.ResourceTable; import com.huawei.cookbook.util.LogUtils; import com.huawei.cookbook.util.PermissionBridge; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.content.Intent; import ohos.agp.components.Component; import ohos.agp.components.ComponentContainer; import ohos.agp.components.DirectionalLayout; import ohos.agp.components.Image; import ohos.agp.components.surfaceprovider.SurfaceProvider; import ohos.agp.graphics.Surface; import ohos.agp.graphics.SurfaceOps; import ohos.agp.window.dialog.ToastDialog; import ohos.app.Context; import ohos.eventhandler.EventHandler; import ohos.eventhandler.EventRunner; import ohos.media.camera.CameraKit; import ohos.media.camera.device.Camera; import ohos.media.camera.device.CameraConfig; import ohos.media.camera.device.CameraStateCallback; import ohos.media.camera.device.FrameConfig; /** * Represents the slice for opening the camera. */ public class OpenCameraSlice extends AbilitySlice implements PermissionBridge.OnPermissionStateListener { private static final String TAG = OpenCameraSlice.class.getName(); private static final int SCREEN_WIDTH = 1080; private static final int SCREEN_HEIGHT = 1920; private static final int SLEEP_TIME = 200; private EventHandler creamEventHandler; private Image exitImage; private SurfaceProvider surfaceProvider; private Image switchCameraImage; private boolean isCameraRear; private Camera cameraDevice; private Surface previewSurface; @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_open_camera); new PermissionBridge().setOnPermissionStateListener(this); } private void initSurface() { surfaceProvider = new SurfaceProvider(this); DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig( ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT); surfaceProvider.setLayoutConfig(params); surfaceProvider.pinToZTop(false); // Add the SurfaceCallBack. surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack()); // Add the SurfaceProvider to the layout. Component component = findComponentById(ResourceTable.Id_surface_container); if (component instanceof ComponentContainer) { ((ComponentContainer) component).addComponent(surfaceProvider); } } private void initControlComponents() { // Create the icon for exiting the photo-taking page. Component exitImageCom = findComponentById(ResourceTable.Id_exit); if (exitImageCom instanceof Image) { exitImage = (Image) exitImageCom; exitImage.setClickedListener(component -> terminate()); } // Create the icon for switching between the front and rear cameras. Component switchCameraImageCom = findComponentById(ResourceTable.Id_switch_camera_btn); if (switchCameraImageCom instanceof Image) { switchCameraImage = (Image) switchCameraImageCom; switchCameraImage.setClickedListener(component -> switchClicked()); } } private void switchClicked() { isCameraRear = !isCameraRear; openCamera(); } private void openCamera() { CameraKit cameraKit = CameraKit.getInstance(getApplicationContext()); String[] cameraLists = cameraKit.getCameraIds(); String cameraId = cameraLists.length > 1 && isCameraRear ? cameraLists[1] : cameraLists[0]; CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl(); cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler); } private void showTips(Context context, String message) { getUITaskDispatcher().asyncDispatch(() -> { ToastDialog toastDialog = new ToastDialog(context); toastDialog.setAutoClosable(false); toastDialog.setContentText(message); toastDialog.show(); }); } @Override public void onPermissionGranted() { getWindow().setTransparent(true); initSurface(); initControlComponents(); creamEventHandler = new EventHandler(EventRunner.create("======CameraBackground")); } @Override public void onPermissionDenied() { showTips(OpenCameraSlice.this, "=======No permission"); } /** * CameraStateCallbackImpl */ class CameraStateCallbackImpl extends CameraStateCallback { CameraStateCallbackImpl() { } @Override public void onCreated(Camera camera) { // Obtain the preview surface. previewSurface = surfaceProvider.getSurfaceOps().get().getSurface(); if (previewSurface == null) { LogUtils.error(TAG, "Creating camera failed. Preview surface is null."); return; } // Wait until the preview surface is created. try { Thread.sleep(SLEEP_TIME); } catch (InterruptedException exception) { LogUtils.warn(TAG, "Waiting to be interrupted"); } CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder(); // Configure the preview surface. cameraConfigBuilder.addSurface(previewSurface); camera.configure(cameraConfigBuilder.build()); cameraDevice = camera; enableImageGroup(); } @Override public void onConfigured(Camera camera) { FrameConfig.Builder framePreviewConfigBuilder = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW); framePreviewConfigBuilder.addSurface(previewSurface); // Start looping capture. camera.triggerLoopingCapture(framePreviewConfigBuilder.build()); } private void enableImageGroup() { if (!exitImage.isEnabled()) { exitImage.setEnabled(true); switchCameraImage.setEnabled(true); } } } /** * SurfaceCallBack */ class SurfaceCallBack implements SurfaceOps.Callback { @Override public void surfaceCreated(SurfaceOps callbackSurfaceOps) { if (callbackSurfaceOps != null) { callbackSurfaceOps.setFixedSize(SCREEN_HEIGHT, SCREEN_WIDTH); } openCamera(); } @Override public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) { } } @Override public void onStop() { cameraDevice.release(); } }
  3. com/huawei/cookbook/util/FaceAuthResult.java
    package com.huawei.cookbook.util; /** * Provides facial recognition return codes. * * @since 2021-04-12 */ public class FaceAuthResult { /** * The facial recognition succeeds. */ public static final int AUTH_SUCCESS = 0; /** * The facial recognition fails. */ public static final int AUTH_FAIL = 1; /** * The facial recognition has been canceled. */ public static final int AUTH_CANCLE = 2; /** * The facial recognition has timed out. */ public static final int AUTH_TIME_OUT = 3; /** * Failed to open the camera. */ public static final int AUTH_OPEN_CAMERA_FAIL = 4; /** * The system is busy, for example, the previous facial recognition is not complete. */ public static final int AUTH_BUSY = 5; /** * An input parameter is incorrect. */ public static final int AUTH_PARAM_ERROR = 6; /** * Facial recognition is locked because the maximum number of recognition failures has been reached. */ public static final int AUTH_FACE_LOCKED = 7; /** * No face image has been added. */ public static final int AUTH_NO_FACE = 8; /** * 2D facial recognition is not supported. */ public static final int AUTH_2D_NOT_SUPPORTED = 9; /** * The security level is not supported. */ public static final int AUTH_SAFE_LEVEL_NOT_SUPPORTED = 10; /** * Non-local authentication is used. */ public static final int AUTH_NOT_LOCAL = 11; /** * Any other error */ public static final int AUTH_OTHER_ERROR = 100; private FaceAuthResult() { super(); } }
  4. com/huawei/cookbook/util/LogUtils.java
    package com.huawei.cookbook.util; import ohos.hiviewdfx.HiLog; import ohos.hiviewdfx.HiLogLabel; /** * LogUtils * * @since 2021-04-12 */ public class LogUtils { private static final String TAG_LOG = "LogUtil"; private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, LogUtils.TAG_LOG); private static final String LOG_FORMAT = "%{public}s: %{public}s"; private LogUtils() { } /** * Print debug log * * @param tag log tag * @param msg log message */ public static void debug(String tag, String msg) { HiLog.debug(LABEL_LOG, LOG_FORMAT, tag, msg); } /** * Print info log * * @param tag log tag * @param msg log message */ public static void info(String tag, String msg) { HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg); } /** * Print warn log * * @param tag log tag * @param msg log message */ public static void warn(String tag, String msg) { HiLog.warn(LABEL_LOG, LOG_FORMAT, tag, msg); } /** * Print error log * * @param tag log tag * @param msg log message */ public static void error(String tag, String msg) { HiLog.error(LABEL_LOG, LOG_FORMAT, tag, msg); } }
  5. com/huawei/cookbook/util/PermissionBridge.java
    package com.huawei.cookbook.util; import ohos.eventhandler.EventHandler; import ohos.eventhandler.EventRunner; import ohos.eventhandler.InnerEvent; /** * PermissionBridge * * @since 2021-04-12 */ public class PermissionBridge { /** * permission handler granted */ public static final int EVENT_PERMISSION_GRANTED = 0x0000023; /** * permission handler denied */ public static final int EVENT_PERMISSION_DENIED = 0x0000024; private static final String TAG = PermissionBridge.class.getSimpleName(); private static OnPermissionStateListener onPermissionStateListener; private static EventHandler handler = new EventHandler(EventRunner.current()) { @Override protected void processEvent(InnerEvent event) { switch (event.eventId) { case EVENT_PERMISSION_GRANTED: onPermissionStateListener.onPermissionGranted(); break; case EVENT_PERMISSION_DENIED: onPermissionStateListener.onPermissionDenied(); break; default: LogUtils.info(TAG, "EventHandler Undefined Event"); break; } } }; /** * setOnPermissionStateListener * * @param permissionStateListener OnPermissionStateListener */ public void setOnPermissionStateListener(OnPermissionStateListener permissionStateListener) { onPermissionStateListener = permissionStateListener; } /** * OnPermissionStateListener * * @since 2021-04-12 */ public interface OnPermissionStateListener { /** * Called when a permission is granted. */ void onPermissionGranted(); /** * Called when the request for a permission is denied. */ void onPermissionDenied(); } /** * Obtains an event handler. * * @return Returns the event handler. */ public static EventHandler getHandler() { return handler; } }
  6. com/huawei/cookbook/MainAbility.java
    package com.huawei.cookbook; import com.huawei.cookbook.slice.MainAbilitySlice; import ohos.aafwk.ability.Ability; import ohos.aafwk.ability.IDataAbilityObserver; import ohos.aafwk.content.Intent; /** * MainAbility * * @since 2021-04-12 */ public class MainAbility extends Ability { /** * Represents a main ability used to obtain a BiometricAuthentication object. */ private static MainAbility myAbility; /** * A constructor used to create a MainAbility instance. */ public MainAbility() { myAbility = this; } /** * Obtains a MainAbility object. * * @return Returns the MainAbility object. */ public static MainAbility getMainAbility() { return myAbility; } @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName()); } }
  7. com/huawei/cookbook/MyApplication.java
    package com.huawei.cookbook; import ohos.aafwk.ability.AbilityPackage; /** * MyApplication * * @since 2021-04-12 */ public class MyApplication extends AbilityPackage { @Override public void onInitialize() { super.onInitialize(); } }
  8. com/huawei/cookbook/OpenCamera.java
    package com.huawei.cookbook; import com.huawei.cookbook.slice.OpenCameraSlice; import com.huawei.cookbook.util.PermissionBridge; import ohos.aafwk.ability.Ability; import ohos.aafwk.content.Intent; import ohos.bundle.IBundleManager; import ohos.security.SystemPermission; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * Represents the ability for opening the camera. * * @since 2021-04-12 */ public class OpenCamera extends Ability { /** * permission handler granted */ private static final int EVENT_PERMISSION_GRANTED = 0x0000023; /** * permission handler denied */ private static final int EVENT_PERMISSION_DENIED = 0x0000024; private static final int PERMISSION_REQUEST_CODE = 0; @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(OpenCameraSlice.class.getName()); requestPermission(); } private void requestPermission() { String[] permissions = { // Storage permission SystemPermission.WRITE_USER_STORAGE, // Camera permission SystemPermission.CAMERA }; List<String> permissionFiltereds = Arrays.stream(permissions) .filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED) .collect(Collectors.toList()); if (permissionFiltereds.isEmpty()) { PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED); return; } requestPermissionsFromUser(permissionFiltereds.toArray(new String[permissionFiltereds.size()]), PERMISSION_REQUEST_CODE); } @Override public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) { if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) { return; } for (int grantResult : grantResults) { if (grantResult != IBundleManager.PERMISSION_GRANTED) { PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_DENIED); terminateAbility(); return; } } PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED); } }
  9. Icon on the camera screen
    Back icon:

Icon for taking a photo:

Icon for switching between the front and rear camera:

Code copied