根据提示做出相应动作，SDK 实时采集动态信息，判断用户是否为活体、真人

## 兼容性
| 条目        | 说明                                                                      |
| ----------- | -----------------------------------------------------------------------  |
| 适配版本    | HarmonyOS 12 及以上版本                                                 |


## 资源引入

### 远程仓库依赖(推荐)
活体检测共享包 HAR 发布在 OpenHarmony 三方库中心仓，需要从 OpenHarmony 三方库中心仓引用

方式一
在 Terminal 窗口中，执行如下命令安装三方包，DevEco Studio 会自动在工程的 oh-package.json5 中自动添加三方包依赖

```
ohpm install @yidun/alive

```
方式二
在工程的 oh-package.json5 中设置三方包依赖

```
"dependencies": {
  "@yidun/alive": "^1.0.3"
}

```
## 各种配置

### 权限配置

共享包依赖相机和文件存储权限，请在 module.json5 中配置

```   
 "requestPermissions": [
      {
        "name": "ohos.permission.CAMERA",
        "reason": "$string:reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.WRITE_MEDIA",
        "reason": "$string:reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.READ_MEDIA",
        "reason": "$string:reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "always"
        }
      }
    ],
```

并且需要在代码中动态授权，具体可参考官方文档 [https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/request-user-authorization-V5](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/request-user-authorization-V5)


## 快速调用示例

### NisCameraPreview 是活体检测自定义组件，需要将它嵌入页面中

#### 注意点

- 为了避免在某些中低端机型上检测卡顿，建议 NisCameraPreview 的宽与高不要设置为全屏，过大的预览控件会导致处理的数据过大，降低检测流畅度
- 预览宽高不要随意设置，请遵守大部分相机支持的预览宽高比，3：4 或 9：16
- 最好限制竖屏，横屏会影响效果
#### 示例

```
import common from '@ohos.app.ability.common'
import { ActionType, AliveDetect, Logger, NisCameraPreview } from '@yidun/alive'

@Component
export default struct Test {
  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
  @State currentTip: string = "请正视前方"
  @State pic: string = "app.media.pic_front_2x"
  private logger: Logger = new Logger("AliveDetectDemo")
  
  initAlive() {
    AliveDetect.getInstance().init(this.context, "844f553a25034a3ab5f38fcd386305d2")
    AliveDetect.getInstance().setDetectedListener({
      onReady: (isInitSuccess: boolean) => {
        if (isInitSuccess) {
          this.logger.debug("引擎初始化成功")
        }
      },
      onActionCommands: (_: ActionType[] | null): void => {
        this.logger.debug("获取配置成功")
      },
      onStateTipChanged: (actionType: ActionType, stateTip: string, _: number): void => {
        switch (actionType) {
          case ActionType.ACTION_ERROR:
            // 更新错误提示语
            this.currentTip = stateTip
            break;
          case ActionType.ACTION_TURN_HEAD_TO_LEFT:
            this.currentTip = stateTip
            this.pic = "app.media.turn_left"
            // 左转头
            break;
          case ActionType.ACTION_TURN_HEAD_TO_RIGHT:
            this.currentTip = stateTip
            this.pic = "app.media.turn_right"
            // 右转头
            break;
          case ActionType.ACTION_OPEN_MOUTH:
            this.currentTip = stateTip
            this.pic = "app.media.open_mouth"
            // 张张嘴
            break;
          case ActionType.ACTION_BLINK_EYES:
            this.currentTip = stateTip
            this.pic = "app.media.open_eyes"
            // 眨眨眼
            break;
        }
      },
      onPassed: (isPassed: boolean, token: string): void => {
        AlertDialog.show({ title: '活体检测', message: `活体检测结果${isPassed} Token is：${token}` })
      },
      onCheck: (): void => {

      },
      onError: (_: number, msg: string): void => {
        // 检测异常
        this.logger.error(msg)
      },
      onOverTime: (): void => {
        // 检测超时
        AlertDialog.show({ title: '活体检测', message: '检测超时了' })
      }
    })
    AliveDetect.getInstance().startDetect()
  }

  onPageShow(): void {
     this.initAlive()
  }
  
  build() {
     RelativeContainer() {
      NisCameraPreview({ previewWidth: 1080, previewHeight: 1440 })
        .alignRules({
          top: { anchor: "statusBar", align: VerticalAlign.Bottom },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })

      Image($r('app.media.alive_bg'))
        .alt('背景图')
        .width('100%')
        .height('100%')
        .objectFit(ImageFit.Contain)
        .alignRules({
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          top: { anchor: '__container__', align: VerticalAlign.Top }
        })

      Image($r(this.pic))
        .alt('动作提示图')
        .width('140vp')
        .height('140vp')
        .interpolation(ImageInterpolation.Medium)
        .objectFit(ImageFit.Contain)
        .margin({
          bottom: '50vp'
        })
        .alignRules({
          bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          right: { anchor: '__container__', align: HorizontalAlign.End }
        })
        .onComplete(() => {
          this.logger.info("动作提示图加载完成")
        })
        .id('action')

      Text(this.currentTip)
        .fontSize('18fp')
        .fontColor('#ff000000')
        .textAlign(TextAlign.Center)
        .fontWeight(600)
        .margin({
          bottom: '16vp'
        })
        .alignRules({
          bottom: { anchor: 'action', align: VerticalAlign.Top },
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          right: { anchor: '__container__', align: HorizontalAlign.End }
        })
    }
    .height('100%')
    .width('100%')
  }
}
```


## 共享包方法说明

首先需要在页面中嵌入 NisCameraPreview 组件，NisCameraPreview 的 id 是 NTSCamera，可以根据需要设置它的位置

```
build() {
    RelativeContainer() {
        NisCameraPreview({ previewWidth: 1080, previewHeight: 1440 })
        .alignRules({
          top: { anchor: "statusBar", align: VerticalAlign.Bottom },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })
    }
}
```

### 1.活体检测初始化

#### 代码说明

```
 AliveDetect.getInstance().init(this.context, "844f553a25034a3ab5f38fcd386305d2")

```

#### 参数说明

|参数|类型|是否必填|默认值|描述|
|----|----|--------|------|----|
|context|common.UIAbilityContext|是|无| 上下文 |
|businessId|String|是|无| 活体检测业务 id |

### 2. 设置回调监听

#### 代码说明
代码添加在 init 之后 startDetect 之前调用


```
AliveDetect.getInstance().setDetectedListener(listener: DetectedListener)

```

#### 参数说明

|参数|类型|是否必填|默认值|描述|
|----|----|--------|------|----|
|listener|DetectedListener|是|无| 监听接口 |

#### DetectedListener 接口说明

```
 interface DetectedListener {
    /**
     * 活体检测引擎初始化时回调
     *
     * @param isInitSuccess 活体检测引擎是否初始化成功：
     *                      1）true，初始化完成可以开始检测
     *                      2）false，初始化失败，可尝试重新启动活体检测流程 {@link AliveDetector#startDetect()}
     */
    onReady: (isInitSuccess: boolean) => void;

    /**
     * 此次活体检测下发的待检测动作指令序列，{@link ActionType}
     *
     * @param actionTypes
     */
     onActionCommands: (actionTypes: ActionType[] | null) => void;

    /**
     * 活体检测状态是否改变，当引擎检测到状态改变时会回调该接口
     *
     * @param actionType 当前动作类型，枚举值，总共6种类型:
     *     ACTION_STRAIGHT_AHEAD("0", "正视前方"),
     *     ACTION_TURN_HEAD_TO_RIGHT("1", "向右转头"),
     *     ACTION_TURN_HEAD_TO_LEFT("2", "向左转头"),
     *     ACTION_OPEN_MOUTH("3", "张嘴动作"),
     *     ACTION_BLINK_EYES("4", "眨眼动作"),
     *     ACTION_ERROR("5", "动作错误"),
     *     ACTION_PASSED("6", "动作通过")
     *                   
     * @param stateTip   引擎检测到的实时状态
     * @param code 错误码，在ACTION_ERROR时用于国际化使用（1：请移动人脸到摄像头视野中间、2：环境光线暗、3：环境光线过亮、4：图像质量模糊）
     */
    onStateTipChanged: (actionType: ActionType, stateTip: string, code: number) => void;

    /**
     * 活体检测是否通过回调
     *
     * @param isPassed 活体检测是否通过，true：通过，false:不通过
     * @param token    此次活体检测返回的易盾token
     */
    onPassed: (isPassed: boolean, token: string) => void;
    /**
     * 活体检测本地检测通过
     * 启动远程检测
     */    
    onCheck: () => void
    /**
     * 活体检测过程中出现错误时回调
     *
     * @param code 错误码
     * -1001：获取配置失败 -1002：打开相机失败 -1003：上传图片失败 -1004：云端检测失败
     * @param msg  出错原因
     */
    onError: (code: number, msg: string) => void;

    /**
     * 活体检测过程超时回调
     */
    onOverTime: () => void
}
```

### 3. 开始活体检测
代码添加在 init 之后调用

#### 代码说明

```
AliveDetect.getInstance().startDetect()

```

### 4. 停止活体检测

#### 代码说明

```
AliveDetect.getInstance().stopDetect()

```