如何使用HarmonyOS面部识别能力 精华

奶盖
发布于 2021-6-15 10:12
浏览
5收藏

1. 介绍

本篇Codelab涉及两个HarmonyOS子系统(生物特征识别相机),详情请参考生物特征识别和相机。本篇要介绍的是在人脸识别认证成功后,跳转到模拟相机的页面的实现方案。在这个应用中,我们通过调用相关接口,检查设备是否具有人脸识别的能力、进行人脸识别、打开相机,从而实现相关的功能。

 🕮 说明
由于人脸录入不开放给三方应用调用,因此进行人脸识别前需要在手机设置中录入人脸信息。

2. 搭建HarmonyOS环境
安装DevEco Studio,详情请参考DevEco Studio下载
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

  1.如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
  2.如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境

🕮 说明
人脸识别需要在真机上运行,因此需要提前申请证书和profile文件,详情请参考申请证书和profile

3. 代码结构解读
本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在参考中提供下载方式,接下来我们会用一小节来讲解整个工程的代码结构。

 ● slice:应用页面

 ◊ MainAbilitySlice:人脸识别的操作界面,包含校验设备是否支持人脸识别功能,人脸识别,人脸识别结果回显以及人脸识别成功后打开相机的功能。
 ◊ OpenCameraSlice:模拟相机的操作页面,包含打卡相机,拍照,存储相片以及切换摄像头的功能。
 ● util:工具类

 ◊ FaceAuthResult:人脸认证结果的返回码对应的常量。
 ◊ LogUtils:日志记录工具类。
 ◊ PermissionBridge:权限申请回调。
 ● resources:存放工程使用到的资源文件。

 ◊ resources\base\layout下存放xml布局文件;
 ◊ resources\base\media下存放图片资源。
 ● config.json:工程相关配置文件。

如何使用HarmonyOS面部识别能力-鸿蒙开发者社区

4. 页面布局
人脸识别页面
本页面主要由DirectionalLayout布局和Button、Text组件共同来构成。其中两个Button组件,作用分别为开始人脸识别和取消人脸识别;两个Text组件,作用分别为显示标题和显示返回的人脸识别结果。在resources\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="生物特征识别" 
        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="开始人脸识别" 
        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="取消人脸识别" 
        ohos:margin="15vp" 
        ohos:text_color="#000000" 
        ohos:text_size="30fp" 
        /> 
</DirectionalLayout>

模拟相机页面
此页面主要由DirectionalLayout、DependentLayout布局和Image组件组成,其中三个Image组件作为图标,左右分别为返回、开始拍照和切换摄像头。在resources\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="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>

 🕮 说明

布局文件中使用到的background_element样式,在entry\src\main\resources\base\graphic下有做定义,详情可以参考完整代码。

5. 相关权限
为了保证应用的成功运行,需要在config.json中声明需要如下权限:

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

此外还需要在OpenCamera的onStart()方法中向用户申请权限,代码示例如下:

private void requestPermission() { 
    String[] permissions = { 
        // 存储权限 
        SystemPermission.WRITE_USER_STORAGE, 
        // 相机权限 
        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); 
}

6. 人脸识别业务逻辑
在人脸识别页面(ability_main.xml)中,我们添加了开始人脸识别和取消人脸识别的Button,通过监听不同Button的点击事件,从而实现不同的业务逻辑。下面我们将分别介绍开始人脸识别和取消人脸识别的业务逻辑。

 

开始人脸识别业务逻辑
在开始人脸识别之前,我们需要校验当前设备(手机)是否具备人脸识别能力,代码示例如下:

private void createStartListener() { 
    // 提示用户人脸识别时将人脸对准摄像头 
    getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true); 
    try { 
        // 创建生物识别对象 
        mBiometricAuthentication =  
            BiometricAuthentication.getInstance(MainAbility.getMainAbility()); 
        // 检验设备是否有人脸识别功能 
        int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability( 
            BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, 
            BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true); 
        if (hasAuth == BiometricAuthentication.BA_CHECK_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 { 
            // 人脸识别不支持或存在其他问题 ,直接在页面显示结果, 
            // 在主线程不需要通过EventHandler发送回显任务 
            int retExcAuth = getRetExcAuth(hasAuth); 
            getAndSetText(ResourceTable.Id_text_status, retExcAuth, true); 
        } 
    } catch (IllegalAccessException e) { 
        LogUtils.error("createStartBtn", "IllegalAccessException when start auth"); 
    } 
}

 🕮 说明

 ● checkAuthenticationAvailability方法参数说明:

        1.BiometricAuthentication.AuthType中有三个类别,分别为

        AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY指纹识别,AUTH_TYPE_BIOMETRIC_FACE_ONLY脸部识别以及AUTH_TYPE_BIOMETRIC_ALL指纹和面部。
 ● BiometricAuthentication.SecureLevel验证级别,3D人脸识别支持S3及以下级别的验证;2D人脸识别支持S2及以下级别的验证

 

由于人脸识别是耗时操作,所以这里新起了线程去做认证,代码示例如下: 

/** 
 * 新建线程进行认证,避免阻塞其他任务 
 */ 
private Runnable runnable = new Runnable() { 
    private void initHandler() { 
        runner = EventRunner.getMainEventRunner(); 
        if (runner == null) { 
            return; 
        } 
        myEventHandle = new MyEventHandle(runner); 
    } 
 
    @Override 
    public void run() { 
        // 初始化myEventHandle 
        initHandler(); 
        // 开始认证 
        startAuth(); 
    } 
};

开始人脸识别,代码示例如下:

private void startAuth() { 
    // retExcAuth 0认证成功 1:比对失败 2:取消认证 3:认证超时 4:打开相机失败 
    // 5:busy,可能上一个认证没有结束 6:入参错误 7:人脸认证锁定(达到错误认证次数了) 
    // 8:没有录入人脸 100:其他错误。 
    int retExcAuth = mBiometricAuthentication.execAuthenticationAction( 
            BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, 
            BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, 
            true, false, null); 
    // 将认证结果发给主线程处理 
    myEventHandler.sendEvent(retExcAuth); 
}

由于我们在线程中执行的人脸识别操作,需要通过EventHandler将识别结果发送到主线程中,并将识别结果显示在页面中,代码示例如下:

/** 
 * 事件分发器 
  */ 
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); 
    } 
}

取消人脸识别
点击取消人脸识别Button,触发取消人脸识别操作,代码示例如下:

private void createCancelBtn() { 
    // 创建点击事件 
    Component component = findComponentById(ResourceTable.Id_button_cancel); 
    // 创建按钮 
    Button cancelBtn = null; 
    if (component != null && component instanceof Button) { 
        cancelBtn = (Button) component; 
        cancelBtn.setClickedListener(view -> { 
            if (mBiometricAuthentication != null) { 
                // 调用取消接口 
                int result = mBiometricAuthentication.cancelAuthenticationAction(); 
                LogUtils.info("createCancelBtn:", result + ""); 
            } 
        }); 
    } 
}

页面跳转
人脸识别成功后,跳转到模拟相机页面,代码示例如下:

private void toAuthAfterPage() { 
    Intent secondIntent = new Intent(); 
    // 指定待启动FA的bundleName和abilityName 
    Operation operation = new Intent.OperationBuilder() 
            .withDeviceId("") 
            .withBundleName(getBundleName()) 
            .withAbilityName(OpenCamera.class.getName()) 
            .build(); 
    secondIntent.setOperation(operation); 
    // startAbility接口实现启动另一个页面 
    startAbility(secondIntent); 
}

7. 相机相关业务逻辑
在模拟相机页面(ability_open_camera.xml)中,包含打开相机和切换前后置摄像头的功能,我们下面将逐一介绍。

初始化SurfaceProvider
用户授权后,开始初始化SurfaceProvider,代码示例如下:

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); 
    // 添加SurfaceCallBack回调 
    surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack()); 
    // 将SurfaceProvider加入到布局中 
    Component component = findComponentById(ResourceTable.Id_surface_container); 
    if (component instanceof ComponentContainer) { 
        ((ComponentContainer) component).addComponent(surfaceProvider); 
    } 
}	

实现SurfaceOps.Callback回调,当Surface创建时,执行打开相机的操作,代码示例如下:

/** 
 * SurfaceCallBack,Surface回调
 */ 
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) { 
    } 
}	
 

打开相机
创建surface后触发surfaceCreated回调,执行打开相机的操作。打开相机并添加相片接收的监听,代码示例如下:

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); 
} 
/** 
 * CameraStateCallbackImpl 相机状态回调 
 */ 
class CameraStateCallbackImpl extends CameraStateCallback { 
    CameraStateCallbackImpl() { 
    } 
 
    @Override 
    public void onCreated(Camera camera) { 
        // 获取预览 
        previewSurface = surfaceProvider.getSurfaceOps().get().getSurface(); 
        if (previewSurface == null) { 
            LogUtils.error(TAG, "create camera filed, 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(); 
        // 配置预览 
        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); 
        // 开启循环捕捉 
        camera.triggerLoopingCapture(framePreviewConfigBuilder.build()); 
    } 
 
    private void enableImageGroup() { 
        if (!exitImage.isEnabled()) { 
            exitImage.setEnabled(true); 
            switchCameraImage.setEnabled(true); 
        } 
    } 
}	

切换前后置摄像头
点击切换摄像头图标后,执行切换前后置摄像头操作,代码示例如下:

private void switchClicked() { 
    isCameraRear = !isCameraRear; 
    openCamera(); 
}	

8. 效果展示
人脸识别FA(MainAbilitySlice)完成了检验设备是否支持人脸识别,人脸识别,人脸识别结果显示,成功后跳转到打开相机的FA(OpenCameraSlice);相机FA实现了相机的打开,拍照,相片存储,摄像头切换的功能。具体效果图如下:
人脸识别初始页面:

如何使用HarmonyOS面部识别能力-鸿蒙开发者社区人脸识别结果显示:

如何使用HarmonyOS面部识别能力-鸿蒙开发者社区

相机页面:

如何使用HarmonyOS面部识别能力-鸿蒙开发者社区

 

9. 完整代码示例
编写布局与样式
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="生物特征识别" 
        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="开始人脸识别" 
        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="取消人脸识别" 
        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>

 

功能逻辑代码
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; 
    /** 
     * 新建线程进行认证,避免阻塞其他任务 
     */ 
    private Runnable runnable = new Runnable() { 
        private void initHandler() { 
            runner = EventRunner.getMainEventRunner(); 
            if (runner == null) { 
                return; 
            } 
            myEventHandle = new MyEventHandle(runner); 
        } 
 
        @Override 
        public void run() { 
            // 初始化myEventHandle 
            initHandler(); 
            // 开始认证 
            startAuth(); 
        } 
    }; 
 
    /** 
     * onStart 
     * 
     * @param intent intent 
     */ 
    @Override 
    public void onStart(Intent intent) { 
        super.onStart(intent); 
        super.setUIContent(ResourceTable.Layout_ability_main); 
        // 创建开始认证按钮,并添加点击事件 
        createStartBtn(); 
        // 创建取消认证按钮,并添加点击事件 
        createCancelBtn(); 
    } 
 
    /** 
     * 创建取消按钮 
     */ 
    private void createCancelBtn() { 
        // 创建点击事件 
        Component component = findComponentById(ResourceTable.Id_button_cancel); 
        // 创建按钮 
        Button cancelBtn = null; 
        if (component != null && component instanceof Button) { 
            cancelBtn = (Button) component; 
            cancelBtn.setClickedListener(view -> { 
                if (mBiometricAuthentication != null) { 
                    // 调用取消接口 
                    int result = mBiometricAuthentication.cancelAuthenticationAction(); 
                    LogUtils.info("createCancelBtn:", result + ""); 
                } 
            }); 
        } 
    } 
 
    /** 
     * 创建开始识别的按钮点击事件 
     */ 
    private void createStartBtn() { 
        // 创建点击事件 
        Component component = findComponentById(ResourceTable.Id_button_start); 
        // 创建按钮 
        Button featureBtn = null; 
        if (component != null && component instanceof Button) { 
            featureBtn = (Button) component; 
            featureBtn.setClickedListener(view -> { 
                createStartListener(); 
            }); 
        } 
    } 
 
    private void createStartListener() { 
        // 提示用户人脸识别时将人脸对准摄像头 
        getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true); 
        try { 
            // 创建生物识别对象 
            mBiometricAuthentication = BiometricAuthentication.getInstance(MainAbility.getMainAbility()); 
            // 检验设备是否有人脸识别功能 
            // BiometricAuthentication.AuthType中有三个类别 
            // 分别为AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY指纹识别 
            // AUTH_TYPE_BIOMETRIC_FACE_ONLY脸部识别 
            // AUTH_TYPE_BIOMETRIC_ALL指纹和面部 
            // BiometricAuthentication.SecureLevel 2D人脸识别建议使用SECURE_LEVEL_S2,3D人脸识别建议使用SECURE_LEVEL_S3 
            int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability( 
                    BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, 
                    BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true); 
 
            // hasAuth 0是支持,1是不支持,2安全级别不支持 3不是本地认证 4无人脸录入 
            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 { 
                // 人脸识别不支持或存在其他问题 ,直接回显页面, 
                // 在主线程不需要通过EventHandler发送回显任务 
                int retExcAuth = getRetExcAuth(hasAuth); 
                getAndSetText(ResourceTable.Id_text_status, retExcAuth, true); 
            } 
        } catch (IllegalAccessException e) { 
            LogUtils.error("createStartBtn", "IllegalAccessException when start auth"); 
        } 
    } 
 
    /** 
     * 开始认证 
     */ 
    private void startAuth() { 
        // retExcAuth 0认证成功 1:比对失败 2:取消认证 3认证超时 4:打开相机失败 
        // 5:busy,可能上一个认证没有结束 6:入参错误 7:人脸认证锁定(达到错误认证次数了) 
        // 8:没有录入人脸 100:其他错误。 
        int retExcAuth = mBiometricAuthentication.execAuthenticationAction( 
                BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY, 
                BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, 
                true, false, null); 
        // 将修改页面发送到主线程执行 
        myEventHandle.sendEvent(retExcAuth); 
    } 
 
    /** 
     * 根据检验是否支持认证返回值获取提示code 
     * 
     * @param hasAuth 是否有认证能力 
     * @return 返回认证码 
     */ 
    private int getRetExcAuth(int hasAuth) { 
        int retExcAuth; 
        if (hasAuth == RET_NOT_SUPPORTED) { 
            // 1是不支持2D人脸识别 
            retExcAuth = FaceAuthResult.AUTH_2D_NOT_SUPPORTED; 
        } else if (hasAuth == RET_SAFE_LEVEL_NOT_SUPPORTED) { 
            // 安全级别不支持 
            retExcAuth = FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED; 
        } else if (hasAuth == RET_NOT_LOCAL) { 
            // 是不是本地认证 
            retExcAuth = FaceAuthResult.AUTH_NOT_LOCAL; 
        } else { 
            // 无人脸录入 
            retExcAuth = FaceAuthResult.AUTH_NO_FACE; 
        } 
        return retExcAuth; 
    } 
 
    /** 
     * 获取并设置text 
     * 
     * @param textId 文本框id 
     * @param retExcAuth 认证返回码 
     * @param isVisible 是否显示 
     */ 
    private void getAndSetText(int textId, int retExcAuth, boolean isVisible) { 
        // 获取状态Text 
        Component componentText = findComponentById(textId); 
        if (componentText != null && componentText instanceof Text) { 
            Text text = (Text) componentText; 
            setTextValueAndColor(retExcAuth, text); 
            if (isVisible) { 
                text.setVisibility(Component.VISIBLE); 
            } 
        } 
    } 
 
    /** 
     * 设置文本提示信息 
     * 
     * @param text 文本对象 
     * @param textValue 文本值 
     * @param color 文本颜色 
     */ 
    private void setTextValueAndColor(Text text, String textValue, Color color) { 
        text.setText(textValue); 
        text.setTextColor(color); 
    } 
 
    /** 
     * 设置文本显示值和文本颜色 
     * 
     * @param retExcAuth 认证返回值 
     * @param text 文本对象 
     */ 
    private void setTextValueAndColor(int retExcAuth, Text text) { 
        switch (retExcAuth) { 
            case FaceAuthResult.AUTH_SUCCESS: 
                setTextValueAndColor(text, "认证成功", Color.GREEN); 
                // 页面跳转 
                toAuthAfterPage(); 
                break; 
            case FaceAuthResult.AUTH_FAIL: 
                setTextValueAndColor(text, "比对失败", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_CANCLE: 
                setTextValueAndColor(text, "取消认证", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_TIME_OUT: 
                setTextValueAndColor(text, "认证超时", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_OPEN_CAMERA_FAIL: 
                setTextValueAndColor(text, "打开相机失败", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_BUSY: 
                setTextValueAndColor(text, "busy,可能上一个认证没有结束", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_PARAM_ERROR: 
                setTextValueAndColor(text, "入参错误", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_FACE_LOCKED: 
                setTextValueAndColor(text, "人脸认证锁定(达到错误认证次数了)", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_NO_FACE: 
                setTextValueAndColor(text, "无人脸录入,请录入人脸。", Color.BLUE); 
                break; 
            case FaceAuthResult.AUTH_OTHER_ERROR: 
                setTextValueAndColor(text, "其他错误。", Color.RED); 
                break; 
            case FaceAuthResult.AUTH_2D_NOT_SUPPORTED: 
                setTextValueAndColor(text, "不支持2D人脸识别。", Color.BLUE); 
                break; 
            case FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED: 
                setTextValueAndColor(text, "安全级别不支持。", Color.BLUE); 
                break; 
            case FaceAuthResult.AUTH_NOT_LOCAL: 
                setTextValueAndColor(text, "不是本地认证。", Color.BLUE); 
                break; 
            default: 
                setTextValueAndColor(text, "开始认证,请将视线对准摄像头。。。。。。。", Color.BLUE); 
                break; 
        } 
    } 
 
    private void toAuthAfterPage() { 
        Intent secondIntent = new Intent(); 
        // 指定待启动FA的bundleName和abilityName 
        Operation operation = new Intent.OperationBuilder() 
                .withDeviceId("") 
                .withBundleName(getBundleName()) 
                .withAbilityName(OpenCamera.class.getName()) 
                .build(); 
        secondIntent.setOperation(operation); 
        // 通过AbilitySlice的startAbility接口实现启动另一个页面 
        startAbility(secondIntent); 
    } 
 
    /** 
     * 事件分发器 
     * 
     * @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;

/**
 * 打开相机slice
 */
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);
        // 添加SurfaceCallBack回调
        surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());
        // 将SurfaceProvider加入到布局中
        Component component = findComponentById(ResourceTable.Id_surface_container);
        if (component instanceof ComponentContainer) {
            ((ComponentContainer) component).addComponent(surfaceProvider);
        }
    }

    private void initControlComponents() {
        // 退出拍照页面图标
        Component exitImageCom = findComponentById(ResourceTable.Id_exit);
        if (exitImageCom instanceof Image) {
            exitImage = (Image) exitImageCom;
            exitImage.setClickedListener(component -> terminate());
        }
        // 切换前后置摄像头图标
        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) {
            // 获取预览
            previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();
            if (previewSurface == null) {
                LogUtils.error(TAG, "create camera filed, 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();
            // 配置预览
            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);
            // 开启循环捕捉
            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; 
 
/** 
 * 人脸认证返回码 
 * 
 * @since 2021-04-12 
 */ 
public class FaceAuthResult { 
    /** 
     * 认证成功 
     */ 
    public static final int AUTH_SUCCESS = 0; 
    /** 
     * 认证失败 
     */ 
    public static final int AUTH_FAIL = 1; 
    /** 
     * 取消认证 
     */ 
    public static final int AUTH_CANCLE = 2; 
    /** 
     * 认证超时 
     */ 
    public static final int AUTH_TIME_OUT = 3; 
    /** 
     * 打开相机失败 
     */ 
    public static final int AUTH_OPEN_CAMERA_FAIL = 4; 
    /** 
     * busy,可能上一个认证没有结束 
     */ 
    public static final int AUTH_BUSY = 5; 
    /** 
     * 入参错误 
     */ 
    public static final int AUTH_PARAM_ERROR = 6; 
    /** 
     * 人脸认证锁定(达到错误认证次数了) 
     */ 
    public static final int AUTH_FACE_LOCKED = 7; 
    /** 
     * 没有录入人脸 
     */ 
    public static final int AUTH_NO_FACE = 8; 
    /** 
     * 不支持2D人脸识别。 
     */ 
    public static final int AUTH_2D_NOT_SUPPORTED = 9; 
    /** 
     * 安全级别不支持 
     */ 
    public static final int AUTH_SAFE_LEVEL_NOT_SUPPORTED = 10; 
    /** 
     * 不是本地认证 
     */ 
    public static final int AUTH_NOT_LOCAL = 11; 
    /** 
     * 其他问题 
     */ 
    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 { 
        /** 
         * 当授权时 
         */ 
        void onPermissionGranted(); 
 
        /** 
         * 当拒绝授权时触发 
         */ 
        void onPermissionDenied(); 
    } 
 
    /** 
     * getHandler 
     * 
     * @return EventHandler 
     */ 
    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 { 
    /** 
     * 声明静态变量,用于获取生物识别对象 
     */ 
    private static MainAbility myAbility; 
 
    /** 
     * 私有构造 
     */ 
    public MainAbility() { 
        myAbility = this; 
    } 
 
    /** 
     * 获取ability 
     * 
     * @return MainAbility 
     */ 
    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; 
 
/** 
 * 打开相机ability 
 * 
 * @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 = { 
            // 存储权限 
            SystemPermission.WRITE_USER_STORAGE, 
            // 相机权限 
            SystemPermission.CAMERA 
        }; 
        List 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.打开相机页面图标
返回图标:

如何使用HarmonyOS面部识别能力-鸿蒙开发者社区

 

拍照图标:

如何使用HarmonyOS面部识别能力-鸿蒙开发者社区

切换前后置摄像头图标:

如何使用HarmonyOS面部识别能力-鸿蒙开发者社区

10. 恭喜您
恭喜你已经完成了基于面容识别的HarmonyOS应用开发的Codelab,并且学到了:

1.人脸识别的开发。
2.相机的打开。
3.线程间通信开发EventHandler

 

已于2022-5-5 14:19:39修改
3
收藏 5
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

人脸识别技术也慢慢普及了

回复
2021-6-15 11:00:00
向金
向金

感谢分享,可以把源码都放到gitee上。

回复
2021-6-16 16:12:34
回复
    相关推荐