[文章]基于HarmonyOS生物特征识别和相机子系统,实现人脸识别和相机拍照功能

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

说明
由于人脸录入不对外开放,因此进行人脸识别前需要在手机设置中录入人脸信息。


2. 搭建HarmonyOS环境

我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。
  • 安装DevEco Studio,详情请参考下载和安装软件。
  • 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
    • 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
    • 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
  • 开发者可以参考以下链接,完成设备调试的相关配置:
    • 使用真机进行调试
    • 使用模拟器进行调试

说明
人脸识别需要在真机上运行

3. 代码结构解读      

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在参考中提供下载方式,接下来我们会用一小节来讲解整个工程的代码结构。
  • slice:应用页面
    • MainAbilitySlice:人脸识别的操作界面,包含校验设备是否支持人脸识别功能,人脸识别,人脸识别结果回显以及人脸识别成功后打开相机的功能。
    • OpenCameraSlice:模拟相机的操作页面,包含打卡相机,拍照,存储相片以及切换摄像头的功能。
  • util:工具类
    • FaceAuthResult:人脸认证结果的返回码对应的常量。
    • LogUtils:日志记录工具类。
    • PermissionBridge:权限申请回调。
  • resources:存放工程使用到的资源文件。
    • resourcesbaselayout下存放xml布局文件;
    • resourcesbasemedia下存放图片资源。
  • config.json:工程相关配置文件。

   
4. 页面布局      
人脸识别页面
本页面主要由DirectionalLayout布局和Button、Text组件共同来构成。其中两个Button组件,作用分别为开始人脸识别和取消人脸识别;两个Text组件,作用分别为显示标题和显示返回的人脸识别结果。在resourceslayoutability_main.xml下有如下代码:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
  4.     ohos:height="match_parent"
  5.     ohos:width="match_parent"
  6.     ohos:orientation="vertical">

  7.     <Text
  8.         ohos:id="$+id:text_helloworld1"
  9.         ohos:height="match_content"
  10.         ohos:width="match_content"
  11.         ohos:background_element="$graphic:background_ability_main"
  12.         ohos:layout_alignment="horizontal_center"
  13.         ohos:left_padding="80vp"
  14.         ohos:right_padding="80vp"
  15.         ohos:text="生物特征识别"
  16.         ohos:text_size="30fp"
  17.         ohos:top_padding="100vp"
  18.         />

  19.     <Text
  20.         ohos:id="$+id:text_status"
  21.         ohos:height="100vp"
  22.         ohos:width="match_parent"
  23.         ohos:background_element="$graphic:background_ability_main"
  24.         ohos:layout_alignment="center"
  25.         ohos:text_alignment="center"
  26.         ohos:max_text_lines="3"
  27.         ohos:multiple_lines="true"
  28.         ohos:margin="5vp"
  29.         ohos:text=""
  30.         ohos:text_font="serif"
  31.         ohos:text_size="30fp"
  32.         ohos:top_padding="5vp"
  33.         ohos:visibility="invisible"
  34.         />

  35.     <Button
  36.         ohos:id="$+id:button_start"
  37.         ohos:height="60vp"
  38.         ohos:width="match_parent"
  39.         ohos:align_parent_bottom="true"
  40.         ohos:background_element="$graphic:button_element"
  41.         ohos:layout_alignment="horizontal_center"
  42.         ohos:left_padding="40vp"
  43.         ohos:right_padding="40vp"
  44.         ohos:text="开始人脸识别"
  45.         ohos:text_color="#000000"
  46.         ohos:text_size="30fp"
  47.         ohos:left_margin="15vp"
  48.         ohos:right_margin="15vp"
  49.         ohos:top_margin="200vp"
  50.        />

  51.     <Button
  52.         ohos:id="$+id:button_cancel"
  53.         ohos:height="60vp"
  54.         ohos:width="match_parent"
  55.         ohos:align_parent_bottom="true"
  56.         ohos:background_element="$graphic:button_element"
  57.         ohos:layout_alignment="horizontal_center"
  58.         ohos:left_padding="40vp"
  59.         ohos:right_padding="40vp"
  60.         ohos:text="取消人脸识别"
  61.         ohos:margin="15vp"
  62.         ohos:text_color="#000000"
  63.         ohos:text_size="30fp"
  64.         />
  65. </DirectionalLayout>
复制代码
模拟相机页面
此页面主要由DirectionalLayout、DependentLayout布局和Image组件组成,其中三个Image组件作为图标,左右分别为返回、开始拍照和切换摄像头。在resourceslayoutability_open_camera.xml下有如下代码:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
  3.                    ohos:height="match_parent"
  4.                    ohos:width="match_parent">

  5.     <DependentLayout
  6.             ohos:id="$+id:root_container"
  7.             ohos:height="match_parent"
  8.             ohos:width="match_parent">

  9.         <DirectionalLayout
  10.                 ohos:id="$+id:surface_container"
  11.                 ohos:height="match_parent"
  12.                 ohos:width="match_parent" />
  13.         <DirectionalLayout
  14.                 ohos:width="match_parent"
  15.                 ohos:height="match_content"
  16.                 ohos:align_parent_bottom="$+id:root_container"
  17.                 ohos:bottom_margin="30vp"
  18.                 ohos:orientation="horizontal">

  19.             <Image
  20.                     ohos:id="$+id:exit"
  21.                     ohos:height="match_content"
  22.                     ohos:width="match_parent"
  23.                     ohos:weight="1"
  24.                     ohos:enabled="false"
  25.                     ohos:layout_alignment="vertical_center"
  26.                     ohos:scale_mode="center"
  27.                     ohos:image_src="$media:ic_camera_back" />

  28.             <Image
  29.                     ohos:id="$+id:tack_picture_btn"
  30.                     ohos:height="match_content"
  31.                     ohos:width="match_parent"
  32.                     ohos:weight="1"
  33.                     ohos:enabled="false"
  34.                     ohos:layout_alignment="vertical_center"
  35.                     ohos:scale_mode="center"
  36.                     ohos:image_src="$media:ic_camera_photo" />

  37.             <Image
  38.                     ohos:id="$+id:switch_camera_btn"
  39.                     ohos:height="match_content"
  40.                     ohos:width="match_parent"
  41.                     ohos:weight="1"
  42.                     ohos:enabled="false"
  43.                     ohos:layout_alignment="vertical_center"
  44.                     ohos:scale_mode="center"
  45.                     ohos:image_src="$media:ic_camera_switch" />
  46.         </DirectionalLayout>
  47.     </DependentLayout>
  48. </DirectionalLayout>
复制代码
布局文件中使用到的background_element样式,在entrysrcmainresourcesbasegraphic下有做定义,详情可以参考完整代码。

5. 相关权限      
为了保证应用的成功运行,需要在config.json中声明需要如下权限:
  1. "reqPermissions": [
  2.       {
  3.         "name": "ohos.permission.ACCESS_BIOMETRIC"
  4.       },
  5.       {
  6.         "name": "ohos.permission.CAMERA"
  7.       },
  8.       {
  9.         "name": "ohos.permission.WRITE_USER_STORAGE"
  10.       }
  11.     ]
复制代码
此外还需要在OpenCamera的onStart()方法中向用户申请权限,代码示例如下:
  1. private void requestPermission() {
  2.     String[] permissions = {
  3.         // 存储权限
  4.         SystemPermission.WRITE_USER_STORAGE,
  5.         // 相机权限
  6.         SystemPermission.CAMERA
  7.     };
  8.     List<String> permissionFiltereds = Arrays.stream(permissions)
  9.             .filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED)
  10.             .collect(Collectors.toList());
  11.     if (permissionFiltereds.isEmpty()) {
  12.         PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED);
  13.         return;
  14.     }
  15.     requestPermissionsFromUser(permissionFiltereds.toArray(new String[permissionFiltereds.size()]),
  16.             PERMISSION_REQUEST_CODE);
  17. }
复制代码

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

开始人脸识别业务逻辑
在开始人脸识别之前,我们需要校验当前设备(手机)是否具备人脸识别能力,代码示例如下:
  1. private void createStartListener() {
  2.     // 提示用户人脸识别时将人脸对准摄像头
  3.     getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true);
  4.     try {
  5.         // 创建生物识别对象
  6.         mBiometricAuthentication =  
  7.           BiometricAuthentication.getInstance(MainAbility.getMainAbility());
  8.         // 检验设备是否有人脸识别功能
  9.         int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability(
  10.             BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,
  11.             BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true);
  12.         if (hasAuth == BiometricAuthentication.BA_CHECK_SUPPORTED) {
  13.             // 如果支持人脸识别,则开启线程进行人脸识别              
  14.             ThreadPoolExecutor pool = new ThreadPoolExecutor(
  15.                 POOL_CORE_SIZE, POOL_MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
  16.                 new LinkedBlockingQueue<>(QUEUE_SIZE), new  
  17.                 ThreadPoolExecutor.DiscardOldestPolicy());
  18.             pool.submit(runnable);
  19.         } else {
  20.             // 人脸识别不支持或存在其他问题 ,直接在页面显示结果,
  21.             // 在主线程不需要通过EventHandler发送回显任务
  22.             int retExcAuth = getRetExcAuth(hasAuth);
  23.             getAndSetText(ResourceTable.Id_text_status, retExcAuth, true);
  24.         }
  25.     } catch (IllegalAccessException e) {
  26.         LogUtils.error("createStartBtn", "IllegalAccessException when start auth");
  27.     }
  28. }
复制代码
说明
  • checkAuthenticationAvailability方法参数说明:
    BiometricAuthentication.AuthType中有三个类别,分别为AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY指纹识别,AUTH_TYPE_BIOMETRIC_FACE_ONLY脸部识别以及AUTH_TYPE_BIOMETRIC_ALL指纹和面部。
  • BiometricAuthentication.SecureLevel验证级别,3D人脸识别支持S3及以下级别的验证;2D人脸识别支持S2及以下级别的验证
由于人脸识别是耗时操作,所以这里新起了线程去做认证,代码示例如下:
  1. /**
  2. * 新建线程进行认证,避免阻塞其他任务
  3. */
  4. private Runnable runnable = new Runnable() {
  5.     private void initHandler() {
  6.         runner = EventRunner.getMainEventRunner();
  7.         if (runner == null) {
  8.             return;
  9.         }
  10.         myEventHandle = new MyEventHandle(runner);
  11.     }

  12.     [url=home.php?mod=space&uid=2735960]@Override[/url]
  13.     public void run() {
  14.         // 初始化myEventHandle
  15.         initHandler();
  16.         // 开始认证
  17.         startAuth();
  18.     }
  19. };
复制代码
开始人脸识别,代码示例如下:
  1. private void startAuth() {
  2.     // retExcAuth 0认证成功 1:比对失败 2:取消认证 3:认证超时 4:打开相机失败
  3.     // 5:busy,可能上一个认证没有结束 6:入参错误 7:人脸认证锁定(达到错误认证次数了)
  4.     // 8:没有录入人脸 100:其他错误。
  5.     int retExcAuth = mBiometricAuthentication.execAuthenticationAction(
  6.             BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,
  7.             BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2,
  8.             true, false, null);
  9.     // 将认证结果发给主线程处理
  10.     myEventHandler.sendEvent(retExcAuth);
  11. }
复制代码
由于我们在线程中执行的人脸识别操作,需要通过EventHandler将识别结果发送到主线程中,并将识别结果显示在页面中,代码示例如下:
  1. /**
  2. * 事件分发器
  3.   */
  4. private class MyEventHandle extends EventHandler {
  5.     MyEventHandle(EventRunner runner) throws IllegalArgumentException {
  6.         super(runner);
  7.     }

  8.     @Override
  9.     protected void processEvent(InnerEvent event) {
  10.         super.processEvent(event);
  11.         int eventId = event.eventId;
  12.         getAndSetText(ResourceTable.Id_text_status, eventId, true);
  13.     }
  14. }
复制代码

取消人脸识别
点击取消人脸识别Button,触发取消人脸识别操作,代码示例如下:
  1. private void createCancelBtn() {
  2.     // 创建点击事件
  3.     Component component = findComponentById(ResourceTable.Id_button_cancel);
  4.     // 创建按钮
  5.     Button cancelBtn = null;
  6.     if (component != null && component instanceof Button) {
  7.         cancelBtn = (Button) component;
  8.         cancelBtn.setClickedListener(view -> {
  9.             if (mBiometricAuthentication != null) {
  10.                 // 调用取消接口
  11.                 int result = mBiometricAuthentication.cancelAuthenticationAction();
  12.                 LogUtils.info("createCancelBtn:", result + "");
  13.             }
  14.         });
  15.     }
  16. }
复制代码

页面跳转
人脸识别成功后,跳转到模拟相机页面,代码示例如下:
  1. private void toAuthAfterPage() {
  2.     Intent secondIntent = new Intent();
  3.     // 指定待启动FA的bundleName和abilityName
  4.     Operation operation = new Intent.OperationBuilder()
  5.             .withDeviceId("")
  6.             .withBundleName(getBundleName())
  7.             .withAbilityName(OpenCamera.class.getName())
  8.             .build();
  9.     secondIntent.setOperation(operation);
  10.     // startAbility接口实现启动另一个页面
  11.     startAbility(secondIntent);
  12. }
复制代码

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

初始化SurfaceProvider
用户授权后,开始初始化SurfaceProvider,代码示例如下:
  1. private void initSurface() {
  2.     surfaceProvider = new SurfaceProvider(this);
  3.     DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig(
  4.             ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);
  5.     surfaceProvider.setLayoutConfig(params);
  6.     surfaceProvider.pinToZTop(false);
  7.     // 添加SurfaceCallBack回调
  8.     surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());
  9.     // 将SurfaceProvider加入到布局中
  10.     Component component = findComponentById(ResourceTable.Id_surface_container);
  11.     if (component instanceof ComponentContainer) {
  12.         ((ComponentContainer) component).addComponent(surfaceProvider);
  13.     }
  14. }
复制代码
实现SurfaceOps.Callback回调,当Surface创建时,执行打开相机的操作,代码示例如下:
  1. /**
  2. * SurfaceCallBack,Surface回调
  3. */
  4. class SurfaceCallBack implements SurfaceOps.Callback {
  5.     @Override
  6.     public void surfaceCreated(SurfaceOps callbackSurfaceOps) {
  7.         if (callbackSurfaceOps != null) {
  8.             callbackSurfaceOps.setFixedSize(SCREEN_HEIGHT, SCREEN_WIDTH);
  9.         }
  10.         openCamera();
  11.     }

  12.     @Override
  13.     public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {
  14.     }

  15.     @Override
  16.     public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {
  17.     }
  18. }
复制代码

打开相机
创建surface后触发surfaceCreated回调,执行打开相机的操作。打开相机并添加相片接收的监听,代码示例如下:
  1. private void openCamera() {
  2.     imageReceiver = ImageReceiver.create(SCREEN_WIDTH, SCREEN_HEIGHT, ImageFormat.JPEG, IMAGE_RCV_CAPACITY);
  3.     // 设置图形到达监听
  4.     imageReceiver.setImageArrivalListener(this::saveImage);
  5.     CameraKit cameraKit = CameraKit.getInstance(getApplicationContext());
  6.     String[] cameraLists = cameraKit.getCameraIds();
  7.     String cameraId = cameraLists.length > 1 && isCameraRear ? cameraLists[1] : cameraLists[0];
  8.     CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();
  9.     cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler);
  10. }
  11. /**
  12. * CameraStateCallbackImpl 相机状态回调
  13. */
  14. class CameraStateCallbackImpl extends CameraStateCallback {
  15.     CameraStateCallbackImpl() {
  16.     }

  17.     @Override
  18.     public void onCreated(Camera camera) {
  19.         // 获取预览
  20.         previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();
  21.         if (previewSurface == null) {
  22.             LogUtils.error(TAG, "create camera failed, preview surface is null");
  23.             return;
  24.         }
  25.         // Wait until the preview surface is created.
  26.         try {
  27.             Thread.sleep(SLEEP_TIME);
  28.         } catch (InterruptedException exception) {
  29.             LogUtils.warn(TAG, "Waiting to be interrupted");
  30.         }
  31.         CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
  32.         // 配置预览
  33.         cameraConfigBuilder.addSurface(previewSurface);
  34.         cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
  35.         camera.configure(cameraConfigBuilder.build());
  36.         cameraDevice = camera;
  37.         enableImageGroup();
  38.         takePictureImage.setEnabled(true);
  39.     }

  40.     @Override
  41.     public void onConfigured(Camera camera) {
  42.         FrameConfig.Builder framePreviewConfigBuilder
  43.                 = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
  44.         framePreviewConfigBuilder.addSurface(previewSurface);
  45.         // 开启循环捕捉
  46.         camera.triggerLoopingCapture(framePreviewConfigBuilder.build());
  47.     }

  48.     private void enableImageGroup() {
  49.         if (!exitImage.isEnabled()) {
  50.             exitImage.setEnabled(true);
  51.             takePictureImage.setEnabled(true);
  52.             switchCameraImage.setEnabled(true);
  53.         }
  54.     }
  55. }
复制代码

切换前后置摄像头
点击切换摄像头图标后,执行切换前后置摄像头操作,代码示例如下:
  1. private void switchClicked() {
  2.     if (!takePictureImage.isEnabled()) {
  3.         return;
  4.     }
  5.     takePictureImage.setEnabled(false);
  6.     isCameraRear = !isCameraRear;
  7.     openCamera();
  8. }
复制代码

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

人脸识别结果显示:

相机页面:

   
9. 恭喜你      
恭喜你已经完成了基于面容识别的HarmonyOS应用开发的Codelab,并且学到了:
  • 人脸识别的开发。
  • 相机的打开。
  • 线程间通信开发EventHandler。
   
10. 完整代码示例      
编写布局与样式
  • base/graphic/background_ability_main.xml
    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
    3.    ohos:shape="rectangle">
    4. <solid
    5.     ohos:color="#FFFFFF"/>
    6. </shape>
    复制代码

  • base/graphic/button_element.xml
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <shape
    3. xmlns:ohos="http://schemas.huawei.com/res/ohos"
    4. ohos:shape="rectangle">
    5. <corners
    6.     ohos:radius="8vp"/>
    7. <solid
    8.     ohos:color="#FF007DFE"/>
    9. </shape>
    复制代码

  • base/layout/ability_main.xml
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <DirectionalLayout
    3. xmlns:ohos="http://schemas.huawei.com/res/ohos"
    4. ohos:height="match_parent"
    5. ohos:width="match_parent"
    6. ohos:orientation="vertical">

    7. <Text
    8.     ohos:id="$+id:text_helloworld1"
    9.     ohos:height="match_content"
    10.     ohos:width="match_content"
    11.     ohos:background_element="$graphic:background_ability_main"
    12.     ohos:layout_alignment="horizontal_center"
    13.     ohos:left_padding="80vp"
    14.     ohos:right_padding="80vp"
    15.     ohos:text="生物特征识别"
    16.     ohos:text_size="30fp"
    17.     ohos:top_padding="100vp"
    18.     />

    19. <Text
    20.     ohos:id="$+id:text_status"
    21.     ohos:height="100vp"
    22.     ohos:width="match_parent"
    23.     ohos:background_element="$graphic:background_ability_main"
    24.     ohos:layout_alignment="center"
    25.     ohos:text_alignment="center"
    26.     ohos:max_text_lines="3"
    27.     ohos:multiple_lines="true"
    28.     ohos:margin="5vp"
    29.     ohos:text=""
    30.     ohos:text_font="serif"
    31.     ohos:text_size="30fp"
    32.     ohos:top_padding="5vp"
    33.     ohos:visibility="invisible"
    34.     />

    35. <Button
    36.     ohos:id="$+id:button_start"
    37.     ohos:height="60vp"
    38.     ohos:width="match_parent"
    39.     ohos:align_parent_bottom="true"
    40.     ohos:background_element="$graphic:button_element"
    41.     ohos:layout_alignment="horizontal_center"
    42.     ohos:left_padding="40vp"
    43.     ohos:right_padding="40vp"
    44.     ohos:text="开始人脸识别"
    45.     ohos:text_color="#000000"
    46.     ohos:text_size="30fp"
    47.     ohos:left_margin="15vp"
    48.     ohos:right_margin="15vp"
    49.     ohos:top_margin="200vp"
    50.    />

    51. <Button
    52.     ohos:id="$+id:button_cancel"
    53.     ohos:height="60vp"
    54.     ohos:width="match_parent"
    55.     ohos:align_parent_bottom="true"
    56.     ohos:background_element="$graphic:button_element"
    57.     ohos:layout_alignment="horizontal_center"
    58.     ohos:left_padding="40vp"
    59.     ohos:right_padding="40vp"
    60.     ohos:text="取消人脸识别"
    61.     ohos:margin="15vp"
    62.     ohos:text_color="#000000"
    63.     ohos:text_size="30fp"
    64.     />
    65. </DirectionalLayout>
    复制代码

  • base/layout/ability_open_camera.xml
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
    3.                ohos:height="match_parent"
    4.                ohos:width="match_parent">

    5. <DependentLayout
    6.     ohos:id="$+id:root_container"
    7.     ohos:height="match_parent"
    8.     ohos:width="match_parent">

    9.     <DirectionalLayout
    10.         ohos:id="$+id:surface_container"
    11.         ohos:height="match_parent"
    12.         ohos:width="match_parent" />
    13.     <DirectionalLayout
    14.         ohos:width="match_parent"
    15.         ohos:height="match_content"
    16.         ohos:align_parent_bottom="$+id:root_container"
    17.         ohos:bottom_margin="30vp"
    18.         ohos:orientation="horizontal">

    19.         <Image
    20.             ohos:id="$+id:exit"
    21.             ohos:height="170px"
    22.             ohos:width="match_parent"
    23.             ohos:weight="1"
    24.             ohos:enabled="false"
    25.             ohos:layout_alignment="vertical_center"
    26.             ohos:scale_mode="center"
    27.             ohos:image_src="$media:ic_camera_back" />

    28.         <Image
    29.             ohos:id="$+id:tack_picture_btn"
    30.             ohos:height="170px"
    31.             ohos:width="match_parent"
    32.             ohos:weight="1"
    33.             ohos:enabled="false"
    34.             ohos:layout_alignment="vertical_center"
    35.             ohos:scale_mode="center"
    36.             ohos:image_src="$media:ic_camera_photo" />

    37.         <Image
    38.             ohos:id="$+id:switch_camera_btn"
    39.             ohos:height="170px"
    40.             ohos:width="match_parent"
    41.             ohos:weight="1"
    42.             ohos:enabled="false"
    43.             ohos:layout_alignment="vertical_center"
    44.             ohos:scale_mode="zoom_center"
    45.             ohos:image_src="$media:ic_camera_switch" />
    46.     </DirectionalLayout>
    47. </DependentLayout>
    48. </DirectionalLayout>
    复制代码

功能逻辑代码
  • com/huawei/cookbook/slice/MainAbilitySlice.java
    1. package com.huawei.cookbook.slice;

    2. import com.huawei.cookbook.MainAbility;
    3. import com.huawei.cookbook.OpenCamera;
    4. import com.huawei.cookbook.ResourceTable;
    5. import com.huawei.cookbook.util.FaceAuthResult;
    6. import com.huawei.cookbook.util.LogUtils;

    7. import ohos.aafwk.ability.AbilitySlice;
    8. import ohos.aafwk.content.Intent;
    9. import ohos.aafwk.content.Operation;
    10. import ohos.agp.components.Button;
    11. import ohos.agp.components.Component;
    12. import ohos.agp.components.Text;
    13. import ohos.agp.utils.Color;
    14. import ohos.biometrics.authentication.BiometricAuthentication;
    15. import ohos.eventhandler.EventHandler;
    16. import ohos.eventhandler.EventRunner;
    17. import ohos.eventhandler.InnerEvent;

    18. import java.util.concurrent.LinkedBlockingQueue;
    19. import java.util.concurrent.ThreadPoolExecutor;
    20. import java.util.concurrent.TimeUnit;

    21. /**
    22. * MainAbilitySlice
    23. *
    24. * @since 2021-04-12
    25. */
    26. public class MainAbilitySlice extends AbilitySlice {
    27. private static final int POOL_CORE_SIZE = 2;
    28. private static final int POOL_MAX_SIZE = 5;
    29. private static final int NO_FACE_RET = -1;
    30. private static final int KEEP_ALIVE_TIME = 3;
    31. private static final int QUEUE_SIZE = 6;
    32. private static final int RET_NOT_SUPPORTED = 1;
    33. private static final int RET_SAFE_LEVEL_NOT_SUPPORTED = 2;
    34. private static final int RET_NOT_LOCAL = 3;
    35. private EventRunner runner;
    36. private MyEventHandle myEventHandle;
    37. private BiometricAuthentication mBiometricAuthentication;
    38. /**
    39. * 新建线程进行认证,避免阻塞其他任务
    40. */
    41. private Runnable runnable = new Runnable() {
    42.     private void initHandler() {
    43.         runner = EventRunner.getMainEventRunner();
    44.         if (runner == null) {
    45.             return;
    46.         }
    47.         myEventHandle = new MyEventHandle(runner);
    48.     }

    49.     @Override
    50.     public void run() {
    51.         // 初始化myEventHandle
    52.         initHandler();
    53.         // 开始认证
    54.         startAuth();
    55.     }
    56. };

    57. /**
    58. * onStart
    59. *
    60. * [url=home.php?mod=space&uid=3142012]@param[/url] intent intent
    61. */
    62. @Override
    63. public void onStart(Intent intent) {
    64.     super.onStart(intent);
    65.     super.setUIContent(ResourceTable.Layout_ability_main);
    66.     // 创建开始认证按钮,并添加点击事件
    67.     createStartBtn();
    68.     // 创建取消认证按钮,并添加点击事件
    69.     createCancelBtn();
    70. }

    71. /**
    72. * 创建取消按钮
    73. */
    74. private void createCancelBtn() {
    75.     // 创建点击事件
    76.     Component component = findComponentById(ResourceTable.Id_button_cancel);
    77.     // 创建按钮
    78.     Button cancelBtn = null;
    79.     if (component != null && component instanceof Button) {
    80.         cancelBtn = (Button) component;
    81.         cancelBtn.setClickedListener(view -> {
    82.             if (mBiometricAuthentication != null) {
    83.                 // 调用取消接口
    84.                 int result = mBiometricAuthentication.cancelAuthenticationAction();
    85.                 LogUtils.info("createCancelBtn:", result + "");
    86.             }
    87.         });
    88.     }
    89. }

    90. /**
    91. * 创建开始识别的按钮点击事件
    92. */
    93. private void createStartBtn() {
    94.     // 创建点击事件
    95.     Component component = findComponentById(ResourceTable.Id_button_start);
    96.     // 创建按钮
    97.     Button featureBtn = null;
    98.     if (component != null && component instanceof Button) {
    99.         featureBtn = (Button) component;
    100.         featureBtn.setClickedListener(view -> {
    101.             createStartListener();
    102.         });
    103.     }
    104. }

    105. private void createStartListener() {
    106.     // 提示用户人脸识别时将人脸对准摄像头
    107.     getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true);
    108.     try {
    109.         // 创建生物识别对象
    110.         mBiometricAuthentication = BiometricAuthentication.getInstance(MainAbility.getMainAbility());
    111.         // 检验设备是否有人脸识别功能
    112.         // BiometricAuthentication.AuthType中有三个类别
    113.         // 分别为AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY指纹识别
    114.         // AUTH_TYPE_BIOMETRIC_FACE_ONLY脸部识别
    115.         // AUTH_TYPE_BIOMETRIC_ALL指纹和面部
    116.         // BiometricAuthentication.SecureLevel 2D人脸识别建议使用SECURE_LEVEL_S2,3D人脸识别建议使用SECURE_LEVEL_S3
    117.         int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability(
    118.                 BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,
    119.                 BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true);

    120.         // hasAuth 0是支持,1是不支持,2安全级别不支持 3不是本地认证 4无人脸录入
    121.         if (hasAuth == 0) {
    122.             ThreadPoolExecutor pool = new ThreadPoolExecutor(
    123.                     POOL_CORE_SIZE, POOL_MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
    124.                     new LinkedBlockingQueue<>(QUEUE_SIZE), new ThreadPoolExecutor.DiscardOldestPolicy());
    125.             pool.submit(runnable);
    126.         } else {
    127.             // 人脸识别不支持或存在其他问题 ,直接回显页面,
    128.             // 在主线程不需要通过EventHandler发送回显任务
    129.             int retExcAuth = getRetExcAuth(hasAuth);
    130.             getAndSetText(ResourceTable.Id_text_status, retExcAuth, true);
    131.         }
    132.     } catch (IllegalAccessException e) {
    133.         LogUtils.error("createStartBtn", "IllegalAccessException when start auth");
    134.     }
    135. }

    136. /**
    137. * 开始认证
    138. */
    139. private void startAuth() {
    140.     // retExcAuth 0认证成功 1:比对失败 2:取消认证 3认证超时 4:打开相机失败
    141.     // 5:busy,可能上一个认证没有结束 6:入参错误 7:人脸认证锁定(达到错误认证次数了)
    142.     // 8:没有录入人脸 100:其他错误。
    143.     int retExcAuth = mBiometricAuthentication.execAuthenticationAction(
    144.             BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,
    145.             BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2,
    146.             true, false, null);
    147.     // 将修改页面发送到主线程执行
    148.     myEventHandle.sendEvent(retExcAuth);
    149. }

    150. /**
    151. * 根据检验是否支持认证返回值获取提示code
    152. *
    153. * @param hasAuth 是否有认证能力
    154. * [url=home.php?mod=space&uid=1141835]@Return[/url] 返回认证码
    155. */
    156. private int getRetExcAuth(int hasAuth) {
    157.     int retExcAuth;
    158.     if (hasAuth == RET_NOT_SUPPORTED) {
    159.         // 1是不支持2D人脸识别
    160.         retExcAuth = FaceAuthResult.AUTH_2D_NOT_SUPPORTED;
    161.     } else if (hasAuth == RET_SAFE_LEVEL_NOT_SUPPORTED) {
    162.         // 安全级别不支持
    163.         retExcAuth = FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED;
    164.     } else if (hasAuth == RET_NOT_LOCAL) {
    165.         // 是不是本地认证
    166.         retExcAuth = FaceAuthResult.AUTH_NOT_LOCAL;
    167.     } else {
    168.         // 无人脸录入
    169.         retExcAuth = FaceAuthResult.AUTH_NO_FACE;
    170.     }
    171.     return retExcAuth;
    172. }

    173. /**
    174. * 获取并设置text
    175. *
    176. * @param textId 文本框id
    177. * @param retExcAuth 认证返回码
    178. * @param isVisible 是否显示
    179. */
    180. private void getAndSetText(int textId, int retExcAuth, boolean isVisible) {
    181.     // 获取状态Text
    182.     Component componentText = findComponentById(textId);
    183.     if (componentText != null && componentText instanceof Text) {
    184.         Text text = (Text) componentText;
    185.         setTextValueAndColor(retExcAuth, text);
    186.         if (isVisible) {
    187.             text.setVisibility(Component.VISIBLE);
    188.         }
    189.     }
    190. }

    191. /**
    192. * 设置文本提示信息
    193. *
    194. * @param text 文本对象
    195. * @param textValue 文本值
    196. * @param color 文本颜色
    197. */
    198. private void setTextValueAndColor(Text text, String textValue, Color color) {
    199.     text.setText(textValue);
    200.     text.setTextColor(color);
    201. }

    202. /**
    203. * 设置文本显示值和文本颜色
    204. *
    205. * @param retExcAuth 认证返回值
    206. * @param text 文本对象
    207. */
    208. private void setTextValueAndColor(int retExcAuth, Text text) {
    209.     switch (retExcAuth) {
    210.         case FaceAuthResult.AUTH_SUCCESS:
    211.             setTextValueAndColor(text, "认证成功", Color.GREEN);
    212.             // 页面跳转
    213.             toAuthAfterPage();
    214.             break;
    215.         case FaceAuthResult.AUTH_FAIL:
    216.             setTextValueAndColor(text, "比对失败", Color.RED);
    217.             break;
    218.         case FaceAuthResult.AUTH_CANCLE:
    219.             setTextValueAndColor(text, "取消认证", Color.RED);
    220.             break;
    221.         case FaceAuthResult.AUTH_TIME_OUT:
    222.             setTextValueAndColor(text, "认证超时", Color.RED);
    223.             break;
    224.         case FaceAuthResult.AUTH_OPEN_CAMERA_FAIL:
    225.             setTextValueAndColor(text, "打开相机失败", Color.RED);
    226.             break;
    227.         case FaceAuthResult.AUTH_BUSY:
    228.             setTextValueAndColor(text, "busy,可能上一个认证没有结束", Color.RED);
    229.             break;
    230.         case FaceAuthResult.AUTH_PARAM_ERROR:
    231.             setTextValueAndColor(text, "入参错误", Color.RED);
    232.             break;
    233.         case FaceAuthResult.AUTH_FACE_LOCKED:
    234.             setTextValueAndColor(text, "人脸认证锁定(达到错误认证次数了)", Color.RED);
    235.             break;
    236.         case FaceAuthResult.AUTH_NO_FACE:
    237.             setTextValueAndColor(text, "无人脸录入,请录入人脸。", Color.BLUE);
    238.             break;
    239.         case FaceAuthResult.AUTH_OTHER_ERROR:
    240.             setTextValueAndColor(text, "其他错误。", Color.RED);
    241.             break;
    242.         case FaceAuthResult.AUTH_2D_NOT_SUPPORTED:
    243.             setTextValueAndColor(text, "不支持2D人脸识别。", Color.BLUE);
    244.             break;
    245.         case FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED:
    246.             setTextValueAndColor(text, "安全级别不支持。", Color.BLUE);
    247.             break;
    248.         case FaceAuthResult.AUTH_NOT_LOCAL:
    249.             setTextValueAndColor(text, "不是本地认证。", Color.BLUE);
    250.             break;
    251.         default:
    252.             setTextValueAndColor(text, "开始认证,请将视线对准摄像头。。。。。。。", Color.BLUE);
    253.             break;
    254.     }
    255. }

    256. private void toAuthAfterPage() {
    257.     Intent secondIntent = new Intent();
    258.     // 指定待启动FA的bundleName和abilityName
    259.     Operation operation = new Intent.OperationBuilder()
    260.             .withDeviceId("")
    261.             .withBundleName(getBundleName())
    262.             .withAbilityName(OpenCamera.class.getName())
    263.             .build();
    264.     secondIntent.setOperation(operation);
    265.     // 通过AbilitySlice的startAbility接口实现启动另一个页面
    266.     startAbility(secondIntent);
    267. }

    268. /**
    269. * 事件分发器
    270. *
    271. * @since 2021-04-12
    272. */
    273. private class MyEventHandle extends EventHandler {
    274.     MyEventHandle(EventRunner runner) throws IllegalArgumentException {
    275.         super(runner);
    276.     }

    277.     @Override
    278.     protected void processEvent(InnerEvent event) {
    279.         super.processEvent(event);
    280.         int eventId = event.eventId;
    281.         getAndSetText(ResourceTable.Id_text_status, eventId, true);
    282.     }
    283. }

    284. @Override
    285. public void onStop() {
    286.     mBiometricAuthentication.cancelAuthenticationAction();
    287.     BiometricAuthentication.AuthenticationTips authenticationTips
    288.             = mBiometricAuthentication.getAuthenticationTips();
    289.     String tips = authenticationTips.tipInfo;
    290. }
    291. }
    复制代码

  • com/huawei/cookbook/slice/OpenCameraSlice.java
    1. package com.huawei.cookbook.slice;

    2. import com.huawei.cookbook.ResourceTable;
    3. import com.huawei.cookbook.util.LogUtils;
    4. import com.huawei.cookbook.util.PermissionBridge;

    5. import ohos.aafwk.ability.AbilitySlice;
    6. import ohos.aafwk.content.Intent;
    7. import ohos.agp.components.Component;
    8. import ohos.agp.components.ComponentContainer;
    9. import ohos.agp.components.DirectionalLayout;
    10. import ohos.agp.components.Image;
    11. import ohos.agp.components.surfaceprovider.SurfaceProvider;
    12. import ohos.agp.graphics.Surface;
    13. import ohos.agp.graphics.SurfaceOps;
    14. import ohos.agp.window.dialog.ToastDialog;
    15. import ohos.app.Context;
    16. import ohos.eventhandler.EventHandler;
    17. import ohos.eventhandler.EventRunner;
    18. import ohos.media.camera.CameraKit;
    19. import ohos.media.camera.device.Camera;
    20. import ohos.media.camera.device.CameraConfig;
    21. import ohos.media.camera.device.CameraStateCallback;
    22. import ohos.media.camera.device.FrameConfig;

    23. /**
    24. * 打开相机slice
    25. */
    26. public class OpenCameraSlice extends AbilitySlice implements PermissionBridge.OnPermissionStateListener {
    27. private static final String TAG = OpenCameraSlice.class.getName();

    28. private static final int SCREEN_WIDTH = 1080;

    29. private static final int SCREEN_HEIGHT = 1920;

    30. private static final int SLEEP_TIME = 200;

    31. private EventHandler creamEventHandler;

    32. private Image exitImage;

    33. private SurfaceProvider surfaceProvider;

    34. private Image switchCameraImage;

    35. private boolean isCameraRear;

    36. private Camera cameraDevice;

    37. private Surface previewSurface;

    38. @Override
    39. public void onStart(Intent intent) {
    40.     super.onStart(intent);
    41.     super.setUIContent(ResourceTable.Layout_ability_open_camera);
    42.     new PermissionBridge().setOnPermissionStateListener(this);
    43. }

    44. private void initSurface() {
    45.     surfaceProvider = new SurfaceProvider(this);
    46.     DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig(
    47.             ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);
    48.     surfaceProvider.setLayoutConfig(params);
    49.     surfaceProvider.pinToZTop(false);
    50.     // 添加SurfaceCallBack回调
    51.     surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());
    52.     // 将SurfaceProvider加入到布局中
    53.     Component component = findComponentById(ResourceTable.Id_surface_container);
    54.     if (component instanceof ComponentContainer) {
    55.         ((ComponentContainer) component).addComponent(surfaceProvider);
    56.     }
    57. }

    58. private void initControlComponents() {
    59.     // 退出拍照页面图标
    60.     Component exitImageCom = findComponentById(ResourceTable.Id_exit);
    61.     if (exitImageCom instanceof Image) {
    62.         exitImage = (Image) exitImageCom;
    63.         exitImage.setClickedListener(component -> terminate());
    64.     }
    65.     // 切换前后置摄像头图标
    66.     Component switchCameraImageCom = findComponentById(ResourceTable.Id_switch_camera_btn);
    67.     if (switchCameraImageCom instanceof Image) {
    68.         switchCameraImage = (Image) switchCameraImageCom;
    69.         switchCameraImage.setClickedListener(component -> switchClicked());
    70.     }
    71. }

    72. private void switchClicked() {
    73.     isCameraRear = !isCameraRear;
    74.     openCamera();
    75. }

    76. private void openCamera() {
    77.     CameraKit cameraKit = CameraKit.getInstance(getApplicationContext());
    78.     String[] cameraLists = cameraKit.getCameraIds();
    79.     String cameraId = cameraLists.length > 1 && isCameraRear ? cameraLists[1] : cameraLists[0];
    80.     CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();
    81.     cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler);
    82. }

    83. private void showTips(Context context, String message) {
    84.     getUITaskDispatcher().asyncDispatch(() -> {
    85.         ToastDialog toastDialog = new ToastDialog(context);
    86.         toastDialog.setAutoClosable(false);
    87.         toastDialog.setContentText(message);
    88.         toastDialog.show();
    89.     });
    90. }

    91. @Override
    92. public void onPermissionGranted() {
    93.     getWindow().setTransparent(true);
    94.     initSurface();
    95.     initControlComponents();
    96.     creamEventHandler = new EventHandler(EventRunner.create("======CameraBackground"));
    97. }

    98. @Override
    99. public void onPermissionDenied() {
    100.     showTips(OpenCameraSlice.this, "=======No permission");
    101. }

    102. /**
    103. * CameraStateCallbackImpl
    104. */
    105. class CameraStateCallbackImpl extends CameraStateCallback {
    106.     CameraStateCallbackImpl() {
    107.     }

    108.     @Override
    109.     public void onCreated(Camera camera) {
    110.         // 获取预览
    111.         previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();
    112.         if (previewSurface == null) {
    113.             LogUtils.error(TAG, "create camera filed, preview surface is null");
    114.             return;
    115.         }
    116.         // Wait until the preview surface is created.
    117.         try {
    118.             Thread.sleep(SLEEP_TIME);
    119.         } catch (InterruptedException exception) {
    120.             LogUtils.warn(TAG, "Waiting to be interrupted");
    121.         }
    122.         CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
    123.         // 配置预览
    124.         cameraConfigBuilder.addSurface(previewSurface);
    125.         camera.configure(cameraConfigBuilder.build());
    126.         cameraDevice = camera;
    127.         enableImageGroup();
    128.     }

    129.     @Override
    130.     public void onConfigured(Camera camera) {
    131.         FrameConfig.Builder framePreviewConfigBuilder
    132.                 = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
    133.         framePreviewConfigBuilder.addSurface(previewSurface);
    134.         // 开启循环捕捉
    135.         camera.triggerLoopingCapture(framePreviewConfigBuilder.build());
    136.     }

    137.     private void enableImageGroup() {
    138.         if (!exitImage.isEnabled()) {
    139.             exitImage.setEnabled(true);
    140.             switchCameraImage.setEnabled(true);
    141.         }
    142.     }
    143. }

    144. /**
    145. * SurfaceCallBack
    146. */
    147. class SurfaceCallBack implements SurfaceOps.Callback {
    148.     @Override
    149.     public void surfaceCreated(SurfaceOps callbackSurfaceOps) {
    150.         if (callbackSurfaceOps != null) {
    151.             callbackSurfaceOps.setFixedSize(SCREEN_HEIGHT, SCREEN_WIDTH);
    152.         }
    153.         openCamera();
    154.     }

    155.     @Override
    156.     public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {
    157.     }

    158.     @Override
    159.     public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {
    160.     }
    161. }

    162. @Override
    163. public void onStop() {
    164.     cameraDevice.release();
    165. }
    166. }
    复制代码

  • com/huawei/cookbook/util/FaceAuthResult.java
    1. package com.huawei.cookbook.util;

    2. /**
    3. * 人脸认证返回码
    4. *
    5. * @since 2021-04-12
    6. */
    7. public class FaceAuthResult {
    8. /**
    9. * 认证成功
    10. */
    11. public static final int AUTH_SUCCESS = 0;
    12. /**
    13. * 认证失败
    14. */
    15. public static final int AUTH_FAIL = 1;
    16. /**
    17. * 取消认证
    18. */
    19. public static final int AUTH_CANCLE = 2;
    20. /**
    21. * 认证超时
    22. */
    23. public static final int AUTH_TIME_OUT = 3;
    24. /**
    25. * 打开相机失败
    26. */
    27. public static final int AUTH_OPEN_CAMERA_FAIL = 4;
    28. /**
    29. * busy,可能上一个认证没有结束
    30. */
    31. public static final int AUTH_BUSY = 5;
    32. /**
    33. * 入参错误
    34. */
    35. public static final int AUTH_PARAM_ERROR = 6;
    36. /**
    37. * 人脸认证锁定(达到错误认证次数了)
    38. */
    39. public static final int AUTH_FACE_LOCKED = 7;
    40. /**
    41. * 没有录入人脸
    42. */
    43. public static final int AUTH_NO_FACE = 8;
    44. /**
    45. * 不支持2D人脸识别。
    46. */
    47. public static final int AUTH_2D_NOT_SUPPORTED = 9;
    48. /**
    49. * 安全级别不支持
    50. */
    51. public static final int AUTH_SAFE_LEVEL_NOT_SUPPORTED = 10;
    52. /**
    53. * 不是本地认证
    54. */
    55. public static final int AUTH_NOT_LOCAL = 11;
    56. /**
    57. * 其他问题
    58. */
    59. public static final int AUTH_OTHER_ERROR = 100;

    60. private FaceAuthResult() {
    61.     super();
    62. }
    63. }
    复制代码

  • com/huawei/cookbook/util/LogUtils.java
    1. package com.huawei.cookbook.util;

    2. import ohos.hiviewdfx.HiLog;
    3. import ohos.hiviewdfx.HiLogLabel;

    4. /**
    5. * LogUtils
    6. *
    7. * @since 2021-04-12
    8. */
    9. public class LogUtils {
    10. private static final String TAG_LOG = "LogUtil";

    11. private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, LogUtils.TAG_LOG);

    12. private static final String LOG_FORMAT = "%{public}s: %{public}s";

    13. private LogUtils() {
    14. }

    15. /**
    16. * Print debug log
    17. *
    18. * @param tag log tag
    19. * @param msg log message
    20. */
    21. public static void debug(String tag, String msg) {
    22.     HiLog.debug(LABEL_LOG, LOG_FORMAT, tag, msg);
    23. }

    24. /**
    25. * Print info log
    26. *
    27. * @param tag log tag
    28. * @param msg log message
    29. */
    30. public static void info(String tag, String msg) {
    31.     HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg);
    32. }

    33. /**
    34. * Print warn log
    35. *
    36. * @param tag log tag
    37. * @param msg log message
    38. */
    39. public static void warn(String tag, String msg) {
    40.     HiLog.warn(LABEL_LOG, LOG_FORMAT, tag, msg);
    41. }

    42. /**
    43. * Print error log
    44. *
    45. * @param tag log tag
    46. * @param msg log message
    47. */
    48. public static void error(String tag, String msg) {
    49.     HiLog.error(LABEL_LOG, LOG_FORMAT, tag, msg);
    50. }
    51. }
    复制代码

  • com/huawei/cookbook/util/PermissionBridge.java
    1. package com.huawei.cookbook.util;

    2. import ohos.eventhandler.EventHandler;
    3. import ohos.eventhandler.EventRunner;
    4. import ohos.eventhandler.InnerEvent;

    5. /**
    6. * PermissionBridge
    7. *
    8. * @since 2021-04-12
    9. */
    10. public class PermissionBridge {
    11. /**
    12. * permission handler granted
    13. */
    14. public static final int EVENT_PERMISSION_GRANTED = 0x0000023;

    15. /**
    16. * permission handler denied
    17. */
    18. public static final int EVENT_PERMISSION_DENIED = 0x0000024;

    19. private static final String TAG = PermissionBridge.class.getSimpleName();

    20. private static OnPermissionStateListener onPermissionStateListener;

    21. private static EventHandler handler = new EventHandler(EventRunner.current()) {
    22.     @Override
    23.     protected void processEvent(InnerEvent event) {
    24.         switch (event.eventId) {
    25.             case EVENT_PERMISSION_GRANTED:
    26.                 onPermissionStateListener.onPermissionGranted();
    27.                 break;
    28.             case EVENT_PERMISSION_DENIED:
    29.                 onPermissionStateListener.onPermissionDenied();
    30.                 break;
    31.             default:
    32.                 LogUtils.info(TAG, "EventHandler Undefined Event");
    33.                 break;
    34.         }
    35.     }
    36. };

    37. /**
    38. * setOnPermissionStateListener
    39. *
    40. * @param permissionStateListener OnPermissionStateListener
    41. */
    42. public void setOnPermissionStateListener(OnPermissionStateListener permissionStateListener) {
    43.     onPermissionStateListener = permissionStateListener;
    44. }

    45. /**
    46. * OnPermissionStateListener
    47. *
    48. * @since 2021-04-12
    49. */
    50. public interface OnPermissionStateListener {
    51.     /**
    52.      * 当授权时
    53.      */
    54.     void onPermissionGranted();

    55.     /**
    56.      * 当拒绝授权时触发
    57.      */
    58.     void onPermissionDenied();
    59. }

    60. /**
    61. * getHandler
    62. *
    63. * @return EventHandler
    64. */
    65. public static EventHandler getHandler() {
    66.     return handler;
    67. }
    68. }
    复制代码

  • com/huawei/cookbook/MainAbility.java
    1. package com.huawei.cookbook;

    2. import com.huawei.cookbook.slice.MainAbilitySlice;

    3. import ohos.aafwk.ability.Ability;
    4. import ohos.aafwk.ability.IDataAbilityObserver;
    5. import ohos.aafwk.content.Intent;

    6. /**
    7. * MainAbility
    8. *
    9. * @since 2021-04-12
    10. */
    11. public class MainAbility extends Ability {
    12. /**
    13. * 声明静态变量,用于获取生物识别对象
    14. */
    15. private static MainAbility myAbility;

    16. /**
    17. * 私有构造
    18. */
    19. public MainAbility() {
    20.     myAbility = this;
    21. }

    22. /**
    23. * 获取ability
    24. *
    25. * @return MainAbility
    26. */
    27. public static MainAbility getMainAbility() {
    28.     return myAbility;
    29. }

    30. @Override
    31. public void onStart(Intent intent) {
    32.     super.onStart(intent);
    33.     super.setMainRoute(MainAbilitySlice.class.getName());
    34. }
    35. }
    复制代码

  • com/huawei/cookbook/MyApplication.java
    1. package com.huawei.cookbook;

    2. import ohos.aafwk.ability.AbilityPackage;

    3. /**
    4. * MyApplication
    5. *
    6. * @since 2021-04-12
    7. */
    8. public class MyApplication extends AbilityPackage {
    9. @Override
    10. public void onInitialize() {
    11.     super.onInitialize();
    12. }
    13. }
    复制代码

  • com/huawei/cookbook/OpenCamera.java
    1. package com.huawei.cookbook;

    2. import com.huawei.cookbook.slice.OpenCameraSlice;
    3. import com.huawei.cookbook.util.PermissionBridge;

    4. import ohos.aafwk.ability.Ability;
    5. import ohos.aafwk.content.Intent;
    6. import ohos.bundle.IBundleManager;
    7. import ohos.security.SystemPermission;

    8. import java.util.Arrays;
    9. import java.util.List;
    10. import java.util.stream.Collectors;

    11. /**
    12. * 打开相机ability
    13. *
    14. * @since 2021-04-12
    15. */
    16. public class OpenCamera extends Ability {
    17. /**
    18. * permission handler granted
    19. */
    20. private static final int EVENT_PERMISSION_GRANTED = 0x0000023;

    21. /**
    22. * permission handler denied
    23. */
    24. private static final int EVENT_PERMISSION_DENIED = 0x0000024;
    25. private static final int PERMISSION_REQUEST_CODE = 0;

    26. @Override
    27. public void onStart(Intent intent) {
    28.     super.onStart(intent);
    29.     super.setMainRoute(OpenCameraSlice.class.getName());
    30.     requestPermission();
    31. }

    32. private void requestPermission() {
    33.     String[] permissions = {
    34.         // 存储权限
    35.         SystemPermission.WRITE_USER_STORAGE,
    36.         // 相机权限
    37.         SystemPermission.CAMERA
    38.     };
    39.     List<String> permissionFiltereds = Arrays.stream(permissions)
    40.             .filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED)
    41.             .collect(Collectors.toList());
    42.     if (permissionFiltereds.isEmpty()) {
    43.         PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED);
    44.         return;
    45.     }
    46.     requestPermissionsFromUser(permissionFiltereds.toArray(new String[permissionFiltereds.size()]),
    47.             PERMISSION_REQUEST_CODE);
    48. }

    49. @Override
    50. public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
    51.     if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) {
    52.         return;
    53.     }
    54.     for (int grantResult : grantResults) {
    55.         if (grantResult != IBundleManager.PERMISSION_GRANTED) {
    56.             PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_DENIED);
    57.             terminateAbility();
    58.             return;
    59.         }
    60.     }
    61.     PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED);
    62. }
    63. }
    复制代码

  • 打开相机页面图标
    返回图标:

拍照图标:

切换前后置摄像头图标:

   

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友