@faceaisdk/react-native-face-sdk 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +252 -0
- package/android/build.gradle +53 -0
- package/android/libs/FaceSDKLib-release.aar +0 -0
- package/android/proguard-rules.pro +3 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/faceaisdk/reactnative/FaceRNModule.kt +383 -0
- package/android/src/main/java/com/faceaisdk/reactnative/FaceRNPackage.kt +16 -0
- package/ios/FaceAISDK/CustomToastView.swift +34 -0
- package/ios/FaceAISDK/FaceAINaviView.swift +212 -0
- package/ios/FaceAISDK/FaceSDKCameraView.swift +40 -0
- package/ios/FaceAISDK/FaceSDKLocalizer.swift +21 -0
- package/ios/FaceAISDK/LivenessDetectView.swift +317 -0
- package/ios/FaceAISDK/ScreenBrightnessHelper.swift +100 -0
- package/ios/FaceAISDK/TTSPlayer.swift +357 -0
- package/ios/FaceAISDK/VerifyFaceView.swift +284 -0
- package/ios/FaceAISDK/addFace/AddFaceByCamera.swift +207 -0
- package/ios/FaceAISDK/addFace/AddFaceByImage.swift +174 -0
- package/ios/FaceAISDK/addFace/ImagePicker.swift +52 -0
- package/ios/FaceAISDK/addFace/VerifyTwoFaceSimiView.swift +210 -0
- package/ios/FaceColorExtensions.swift +10 -0
- package/ios/FaceRNModule.h +9 -0
- package/ios/FaceRNModule.m +197 -0
- package/ios/FaceSDKSwiftManager.swift +277 -0
- package/ios/Resources/en.lproj/Localizable.strings +51 -0
- package/ios/Resources/light_too_high.png +0 -0
- package/ios/Resources/zh-Hans.lproj/Localizable.strings +51 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.js +112 -0
- package/lib/types.d.ts +39 -0
- package/lib/types.js +2 -0
- package/package.json +88 -0
- package/react-native-face-sdk.podspec +28 -0
- package/src/index.ts +184 -0
- package/src/types.ts +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Email:FaceAISDK.Service@gmail.com
|
package/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# @faceaisdk/react-native-face-sdk
|
|
2
|
+
|
|
3
|
+
FaceAISDK 人脸识别、活体检测 React Native 原生插件仓库,支持 iOS 和 Android 双端,所有功能无需后台 API 服务即可离线运行。
|
|
4
|
+
|
|
5
|
+
当前仓库已调整为更接近标准 RN 库仓的结构:
|
|
6
|
+
|
|
7
|
+
- **根目录**:可发布插件 `@faceaisdk/react-native-face-sdk`
|
|
8
|
+
- **`example/`**:示例 App / 真机联调工程
|
|
9
|
+
|
|
10
|
+
> **说明**:人脸识别、活体检测等功能需要摄像头,必须使用真机,不能在模拟器中验证。
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @faceaisdk/react-native-face-sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### iOS
|
|
19
|
+
|
|
20
|
+
在您的 iOS 工程目录下运行:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
cd ios
|
|
24
|
+
pod install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **注意**:由于使用了人脸识别 SDK,您需要在 `Info.plist` 中添加相机权限描述:
|
|
28
|
+
> ```xml
|
|
29
|
+
> <key>NSCameraUsageDescription</key>
|
|
30
|
+
> <string>我们需要访问您的相机进行人脸识别</string>
|
|
31
|
+
> ```
|
|
32
|
+
|
|
33
|
+
### Android
|
|
34
|
+
|
|
35
|
+
1. 确保您的项目 `minSdkVersion` 至少为 24。
|
|
36
|
+
2. Android 端会自动完成 Autolinking。
|
|
37
|
+
|
|
38
|
+
> **注意**:由于使用了人脸识别 SDK,您需要在 `AndroidManifest.xml` 中确保有以下权限:
|
|
39
|
+
> ```xml
|
|
40
|
+
> <uses-permission android:name="android.permission.CAMERA" />
|
|
41
|
+
> ```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 功能列表
|
|
46
|
+
|
|
47
|
+
| 功能 | iOS | Android |
|
|
48
|
+
|------|-----|---------|
|
|
49
|
+
| SDK相机录入人脸信息 | ✅ | ✅ |
|
|
50
|
+
| 1:1人脸识别+活体检测 | ✅ | ✅ |
|
|
51
|
+
| 活体检测(动作/炫彩/静默) | ✅ | ✅ |
|
|
52
|
+
| 查询人脸特征信息 | ✅ | ✅ |
|
|
53
|
+
| 同步人脸特征信息 | ✅ | ✅ |
|
|
54
|
+
| 图片录入人脸信息 | ✅ | ✅ |
|
|
55
|
+
| 删除人脸特征信息 | ✅ | ✅ |
|
|
56
|
+
|
|
57
|
+
## 仓库结构
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
FaceAISDK_RN/
|
|
61
|
+
├── src/ # 插件 TypeScript 对外 API
|
|
62
|
+
├── android/ # 插件 Android Library 工程
|
|
63
|
+
├── ios/ # 插件 iOS 原生源码与资源
|
|
64
|
+
├── @faceaisdk/react-native-face-sdk.podspec # iOS Podspec
|
|
65
|
+
├── example/ # 示例 App(真机调试、联调、回归)
|
|
66
|
+
│ ├── App.tsx
|
|
67
|
+
│ ├── android/
|
|
68
|
+
│ ├── ios/
|
|
69
|
+
│ └── auto_run.sh
|
|
70
|
+
├── __tests__/ # 插件单元测试
|
|
71
|
+
├── README.md
|
|
72
|
+
└── 插件封装与npm发布指南.md
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## JS API
|
|
76
|
+
|
|
77
|
+
根目录导出 Promise 风格接口:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import {
|
|
81
|
+
addFaceBySDKCamera,
|
|
82
|
+
faceVerify,
|
|
83
|
+
livenessVerify,
|
|
84
|
+
getFaceFeature,
|
|
85
|
+
insertFaceFeature,
|
|
86
|
+
addFaceByImage,
|
|
87
|
+
deleteFaceFeature,
|
|
88
|
+
} from '@faceaisdk/react-native-face-sdk';
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 核心方法说明
|
|
92
|
+
|
|
93
|
+
#### 1. SDK 相机录入人脸
|
|
94
|
+
`addFaceBySDKCamera(faceID, options)`
|
|
95
|
+
- `faceID`: 用户唯一标识
|
|
96
|
+
- `options`: `{ mode?: number, showConfirm?: boolean }`
|
|
97
|
+
|
|
98
|
+
#### 2. 人脸比对 + 活体检测
|
|
99
|
+
`faceVerify(faceID, options)`
|
|
100
|
+
- `faceID`: 待验证的用户标识
|
|
101
|
+
- `options`: 包含阈值、活体类型、动作序列、超时时间等配置
|
|
102
|
+
|
|
103
|
+
#### 3. 纯活体检测
|
|
104
|
+
`livenessVerify(options)`
|
|
105
|
+
- 仅检测镜头前是否为活人,不进行 1:1 比对
|
|
106
|
+
|
|
107
|
+
## 统一返回结构
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
export interface FaceResult {
|
|
111
|
+
code: number;
|
|
112
|
+
msg: string;
|
|
113
|
+
faceID: string;
|
|
114
|
+
similarity: number;
|
|
115
|
+
liveness: number;
|
|
116
|
+
faceFeature: string;
|
|
117
|
+
faceBase64: string;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 状态码说明
|
|
122
|
+
|
|
123
|
+
| Code | 含义 |
|
|
124
|
+
|------|------|
|
|
125
|
+
| 0 | 初始化状态/用户取消 |
|
|
126
|
+
| 1 | 人脸识别对比成功(大于设置的 threshold) |
|
|
127
|
+
| 2 | 人脸识别对比失败(小于设置的 threshold) |
|
|
128
|
+
| 3 | 动作活体检测成功 |
|
|
129
|
+
| 4 | 动作活体超时 |
|
|
130
|
+
| 5 | 多次没有检测到人脸 |
|
|
131
|
+
| 6 | 没有对应的人脸特征值 |
|
|
132
|
+
| 7 | 炫彩活体成功 |
|
|
133
|
+
| 8 | 炫彩活体失败 |
|
|
134
|
+
| 9 | 炫彩活体失败(光线亮度过高) |
|
|
135
|
+
| 10 | 所有活体检测完成 |
|
|
136
|
+
| 11 | 静默活体检测失败 |
|
|
137
|
+
| 12 | 没有录入人脸信息 |
|
|
138
|
+
| 13 | 多人脸出现在镜头 |
|
|
139
|
+
|
|
140
|
+
## 根目录:作为库开发/发布
|
|
141
|
+
|
|
142
|
+
### 构建与测试
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
npm run typecheck
|
|
146
|
+
npm run build
|
|
147
|
+
npm test
|
|
148
|
+
npm run pack
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 发布前检查
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
npm pack .
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
生成产物中应至少包含:
|
|
158
|
+
|
|
159
|
+
- `src/` / `lib/`
|
|
160
|
+
- `android/`
|
|
161
|
+
- `ios/`
|
|
162
|
+
- `@faceaisdk/react-native-face-sdk.podspec`
|
|
163
|
+
|
|
164
|
+
## `example/`:作为示例 App 运行
|
|
165
|
+
|
|
166
|
+
> 请确保已完成 [React Native 环境配置](https://reactnative.dev/docs/set-up-your-environment)。
|
|
167
|
+
|
|
168
|
+
### 方式一:使用仓库根命令
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
npm run start
|
|
172
|
+
npm run android
|
|
173
|
+
npm run ios
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
这些命令会自动代理到 `example/` 工程。
|
|
177
|
+
|
|
178
|
+
### 方式二:直接进入 `example/`
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
cd example
|
|
182
|
+
node ../node_modules/react-native/cli.js start --config metro.config.js
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Android
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
cd example
|
|
189
|
+
node ../node_modules/react-native/cli.js run-android --no-packager
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### iOS
|
|
193
|
+
|
|
194
|
+
```sh
|
|
195
|
+
cd example
|
|
196
|
+
./pod-install.sh
|
|
197
|
+
node ../node_modules/react-native/cli.js run-ios --device "您的手机名称"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
如果您希望在仓库根目录执行,也可以直接运行:
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
npm run pods:install
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`pod-install.sh` 会自动:
|
|
207
|
+
|
|
208
|
+
- 使用 `example/vendor/bundle` 安装 Ruby gems
|
|
209
|
+
- 注入 Ruby 4 所需的 `kconv` 兼容层
|
|
210
|
+
- 对 `RCTSwiftUI` / `TensorFlowLiteSwift` 注入 `-no-verify-emitted-module-interface` 兼容参数(用于 Xcode 15.x)
|
|
211
|
+
- 执行 `bundle exec pod install`
|
|
212
|
+
|
|
213
|
+
### 真机自动运行脚本
|
|
214
|
+
|
|
215
|
+
根目录保留了快捷入口,仍可直接执行:
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
./auto_run.sh
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
实际会转发到:
|
|
222
|
+
|
|
223
|
+
```sh
|
|
224
|
+
./example/auto_run.sh
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## 示例工程如何消费本地库
|
|
228
|
+
|
|
229
|
+
`example/App.tsx` 使用包名方式调用本地根库:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import {faceVerify} from '@faceaisdk/react-native-face-sdk';
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
并通过 `example/metro.config.js` 将该包名解析到仓库根目录源码,便于在本地开发时即时调试。
|
|
236
|
+
|
|
237
|
+
## 常见问题 (Troubleshooting)
|
|
238
|
+
|
|
239
|
+
### 1. 权限问题
|
|
240
|
+
本插件需要相机权限。
|
|
241
|
+
- **Android**: 插件会自动处理运行时权限请求,但您仍需确保 `AndroidManifest.xml` 中声明了权限。
|
|
242
|
+
- **iOS**: 请务必在 `Info.plist` 中配置 `NSCameraUsageDescription`,否则 App 会在调用相机时崩溃。
|
|
243
|
+
|
|
244
|
+
### 2. 只有真机可用
|
|
245
|
+
人脸识别和活体检测依赖底层硬件和原生 SDK,**模拟器无法运行**,请使用真机测试。
|
|
246
|
+
|
|
247
|
+
### 3. 编译错误 (Xcode 15+)
|
|
248
|
+
如果遇到 Swift 相关的编译错误,请参考 `example/pod-install.sh` 中的处理方式,确保开启了 `-no-verify-emitted-module-interface`。
|
|
249
|
+
|
|
250
|
+
## 许可证 (License)
|
|
251
|
+
|
|
252
|
+
MIT
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
apply plugin: 'org.jetbrains.kotlin.android'
|
|
3
|
+
|
|
4
|
+
android {
|
|
5
|
+
namespace 'com.faceaisdk.reactnative'
|
|
6
|
+
compileSdkVersion rootProject.ext.has('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
7
|
+
|
|
8
|
+
defaultConfig {
|
|
9
|
+
minSdkVersion rootProject.ext.has('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
10
|
+
targetSdkVersion rootProject.ext.has('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
11
|
+
consumerProguardFiles 'proguard-rules.pro'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
compileOptions {
|
|
15
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
16
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//SDK依赖Java11 就够了
|
|
20
|
+
kotlinOptions {
|
|
21
|
+
jvmTarget = '17'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
sourceSets {
|
|
25
|
+
main {
|
|
26
|
+
manifest.srcFile 'src/main/AndroidManifest.xml'
|
|
27
|
+
java.srcDirs = ['src/main/java']
|
|
28
|
+
res.srcDirs = ['src/main/res']
|
|
29
|
+
jniLibs.srcDirs = ['libs']
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
repositories {
|
|
35
|
+
google()
|
|
36
|
+
mavenCentral()
|
|
37
|
+
maven { url 'https://jitpack.io' }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
dependencies {
|
|
41
|
+
implementation 'com.facebook.react:react-android'
|
|
42
|
+
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
|
43
|
+
|
|
44
|
+
implementation 'io.github.faceaisdk:Android:2026.06.21'
|
|
45
|
+
implementation 'com.tencent:mmkv:1.3.14'
|
|
46
|
+
implementation 'com.airbnb.android:lottie:6.5.2'
|
|
47
|
+
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
|
48
|
+
implementation 'androidx.appcompat:appcompat:1.6.0'
|
|
49
|
+
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
|
50
|
+
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
|
51
|
+
implementation 'pub.devrel:easypermissions:3.0.0'
|
|
52
|
+
}
|
|
53
|
+
|
|
Binary file
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
package com.faceaisdk.reactnative
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.content.pm.PackageManager
|
|
7
|
+
import android.Manifest
|
|
8
|
+
import androidx.core.app.ActivityCompat
|
|
9
|
+
import androidx.core.content.ContextCompat
|
|
10
|
+
import com.facebook.react.bridge.*
|
|
11
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
12
|
+
import com.facebook.react.modules.core.PermissionAwareActivity
|
|
13
|
+
import com.facebook.react.modules.core.PermissionListener
|
|
14
|
+
import com.faceAI.demo.FaceSDKConfig
|
|
15
|
+
import com.faceAI.demo.SysCamera.addFace.AddFaceFeatureActivity
|
|
16
|
+
import com.faceAI.demo.SysCamera.verify.FaceVerificationActivity
|
|
17
|
+
import com.faceAI.demo.SysCamera.verify.LivenessDetectActivity
|
|
18
|
+
import com.faceAI.demo.base.utils.BitmapUtils
|
|
19
|
+
import com.ai.face.faceSearch.search.Image2FaceFeature
|
|
20
|
+
import android.graphics.Bitmap
|
|
21
|
+
import android.text.TextUtils
|
|
22
|
+
import com.tencent.mmkv.MMKV
|
|
23
|
+
|
|
24
|
+
class FaceRNModule(reactContext: ReactApplicationContext) :
|
|
25
|
+
ReactContextBaseJavaModule(reactContext), ActivityEventListener {
|
|
26
|
+
|
|
27
|
+
companion object {
|
|
28
|
+
const val NAME = "FaceRNModule"
|
|
29
|
+
private const val REQ_CODE_ADD_FACE = 10086
|
|
30
|
+
private const val REQ_CODE_VERIFY = 10087
|
|
31
|
+
private const val REQ_CODE_LIVENESS = 10088
|
|
32
|
+
private const val PERMISSION_REQUEST_CODE = 10010
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private var mCallback: Callback? = null
|
|
36
|
+
private var mCurrentFaceID: String = ""
|
|
37
|
+
private var mPendingAction: (() -> Unit)? = null
|
|
38
|
+
|
|
39
|
+
init {
|
|
40
|
+
reactContext.addActivityEventListener(this)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
override fun getName(): String = NAME
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 检查并请求相机权限
|
|
47
|
+
*/
|
|
48
|
+
private fun checkCameraPermission(action: () -> Unit) {
|
|
49
|
+
val activity = reactApplicationContext.currentActivity ?: return
|
|
50
|
+
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
|
|
51
|
+
== PackageManager.PERMISSION_GRANTED
|
|
52
|
+
) {
|
|
53
|
+
action()
|
|
54
|
+
} else {
|
|
55
|
+
mPendingAction = action
|
|
56
|
+
val permissionAwareActivity = activity as? PermissionAwareActivity
|
|
57
|
+
permissionAwareActivity?.requestPermissions(
|
|
58
|
+
arrayOf(Manifest.permission.CAMERA),
|
|
59
|
+
PERMISSION_REQUEST_CODE,
|
|
60
|
+
object : PermissionListener {
|
|
61
|
+
override fun onRequestPermissionsResult(
|
|
62
|
+
requestCode: Int,
|
|
63
|
+
permissions: Array<String>,
|
|
64
|
+
grantResults: IntArray
|
|
65
|
+
): Boolean {
|
|
66
|
+
if (requestCode == PERMISSION_REQUEST_CODE) {
|
|
67
|
+
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
68
|
+
mPendingAction?.invoke()
|
|
69
|
+
} else {
|
|
70
|
+
mCallback?.invoke(Arguments.createMap().apply {
|
|
71
|
+
putInt("code", -1)
|
|
72
|
+
putString("msg", "相机权限被拒绝,请在设置中开启")
|
|
73
|
+
putString("faceID", mCurrentFaceID)
|
|
74
|
+
})
|
|
75
|
+
mCallback = null
|
|
76
|
+
}
|
|
77
|
+
mPendingAction = null
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 1. SDK相机录入人脸信息
|
|
89
|
+
*/
|
|
90
|
+
@ReactMethod
|
|
91
|
+
fun addFaceBySDKCamera(
|
|
92
|
+
faceID: String,
|
|
93
|
+
addFacePerformanceMode: Int,
|
|
94
|
+
needShowConfirmDialog: Boolean,
|
|
95
|
+
callback: Callback
|
|
96
|
+
) {
|
|
97
|
+
val activity = reactApplicationContext.currentActivity ?: return
|
|
98
|
+
mCallback = callback
|
|
99
|
+
mCurrentFaceID = faceID
|
|
100
|
+
|
|
101
|
+
checkCameraPermission {
|
|
102
|
+
FaceSDKConfig.init(activity)
|
|
103
|
+
val intent = Intent()
|
|
104
|
+
intent.setClassName(
|
|
105
|
+
activity,
|
|
106
|
+
"com.faceAI.demo.SysCamera.addFace.AddFaceFeatureActivity"
|
|
107
|
+
)
|
|
108
|
+
intent.putExtra("ADD_FACE_IMAGE_TYPE_KEY", "FACE_VERIFY")
|
|
109
|
+
intent.putExtra("USER_FACE_ID_KEY", faceID)
|
|
110
|
+
intent.putExtra("NEED_CONFIRM_ADD_FACE", needShowConfirmDialog)
|
|
111
|
+
intent.putExtra("ADD_FACE_PERFORMANCE_MODE", addFacePerformanceMode)
|
|
112
|
+
activity.startActivityForResult(intent, REQ_CODE_ADD_FACE)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 2. 人脸识别+活体检测
|
|
118
|
+
*/
|
|
119
|
+
@ReactMethod
|
|
120
|
+
fun faceVerify(
|
|
121
|
+
faceID: String,
|
|
122
|
+
threshold: Double,
|
|
123
|
+
faceLivenessType: Int,
|
|
124
|
+
motionLivenessTypes: String,
|
|
125
|
+
motionLivenessTimeOut: Int,
|
|
126
|
+
motionLivenessSteps: Int,
|
|
127
|
+
allowMultiFaces: Boolean,
|
|
128
|
+
callback: Callback
|
|
129
|
+
) {
|
|
130
|
+
val activity = reactApplicationContext.currentActivity ?: return
|
|
131
|
+
mCallback = callback
|
|
132
|
+
mCurrentFaceID = faceID
|
|
133
|
+
|
|
134
|
+
checkCameraPermission {
|
|
135
|
+
FaceSDKConfig.init(activity)
|
|
136
|
+
val intent = Intent()
|
|
137
|
+
intent.setClassName(
|
|
138
|
+
activity,
|
|
139
|
+
"com.faceAI.demo.SysCamera.verify.FaceVerificationActivity"
|
|
140
|
+
)
|
|
141
|
+
intent.putExtra(FaceVerificationActivity.USER_FACE_ID_KEY, faceID)
|
|
142
|
+
intent.putExtra(FaceVerificationActivity.THRESHOLD_KEY, threshold.toFloat())
|
|
143
|
+
intent.putExtra(FaceVerificationActivity.FACE_LIVENESS_TYPE, faceLivenessType)
|
|
144
|
+
intent.putExtra(FaceVerificationActivity.MOTION_LIVENESS_TYPES, motionLivenessTypes)
|
|
145
|
+
intent.putExtra(FaceVerificationActivity.MOTION_TIMEOUT, motionLivenessTimeOut)
|
|
146
|
+
intent.putExtra(FaceVerificationActivity.MOTION_STEP_SIZE, motionLivenessSteps)
|
|
147
|
+
intent.putExtra(FaceVerificationActivity.ALLOW_MULTI_FACES, allowMultiFaces)
|
|
148
|
+
activity.startActivityForResult(intent, REQ_CODE_VERIFY)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 3. 活体检测
|
|
154
|
+
*/
|
|
155
|
+
@ReactMethod
|
|
156
|
+
fun livenessVerify(
|
|
157
|
+
faceLivenessType: Int,
|
|
158
|
+
motionLivenessTypes: String,
|
|
159
|
+
motionLivenessTimeOut: Int,
|
|
160
|
+
motionLivenessSteps: Int,
|
|
161
|
+
allowMultiFaces: Boolean,
|
|
162
|
+
showResultTips: Boolean,
|
|
163
|
+
callback: Callback
|
|
164
|
+
) {
|
|
165
|
+
val activity = reactApplicationContext.currentActivity ?: return
|
|
166
|
+
mCallback = callback
|
|
167
|
+
mCurrentFaceID = ""
|
|
168
|
+
|
|
169
|
+
checkCameraPermission {
|
|
170
|
+
FaceSDKConfig.init(activity)
|
|
171
|
+
val intent = Intent()
|
|
172
|
+
intent.setClassName(
|
|
173
|
+
activity,
|
|
174
|
+
"com.faceAI.demo.SysCamera.verify.LivenessDetectActivity"
|
|
175
|
+
)
|
|
176
|
+
intent.putExtra(LivenessDetectActivity.FACE_LIVENESS_TYPE, faceLivenessType)
|
|
177
|
+
intent.putExtra(LivenessDetectActivity.MOTION_LIVENESS_TYPES, motionLivenessTypes)
|
|
178
|
+
intent.putExtra(LivenessDetectActivity.MOTION_TIMEOUT, motionLivenessTimeOut)
|
|
179
|
+
intent.putExtra(LivenessDetectActivity.MOTION_STEP_SIZE, motionLivenessSteps)
|
|
180
|
+
intent.putExtra(LivenessDetectActivity.ALLOW_MULTI_FACES, allowMultiFaces)
|
|
181
|
+
intent.putExtra(LivenessDetectActivity.SHOW_RESULT_TIPS, showResultTips) //是否提示活体检测结果还是调用方待后续动作处理完毕后自行处理
|
|
182
|
+
activity.startActivityForResult(intent, REQ_CODE_LIVENESS)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 4. 查询人脸特征信息
|
|
188
|
+
*/
|
|
189
|
+
@ReactMethod
|
|
190
|
+
fun getFaceFeature(faceID: String, callback: Callback) {
|
|
191
|
+
val context = reactApplicationContext.applicationContext
|
|
192
|
+
FaceSDKConfig.init(context)
|
|
193
|
+
|
|
194
|
+
val faceFeature = MMKV.defaultMMKV().decodeString(faceID)
|
|
195
|
+
val result = Arguments.createMap()
|
|
196
|
+
|
|
197
|
+
if (faceFeature.isNullOrEmpty()) {
|
|
198
|
+
result.putInt("code", 0)
|
|
199
|
+
result.putString("msg", "Face Feature not exist")
|
|
200
|
+
result.putString("faceFeature", "")
|
|
201
|
+
} else if (faceFeature.length != 1024) {
|
|
202
|
+
result.putInt("code", 0)
|
|
203
|
+
result.putString("msg", "Face Feature length should be 1024")
|
|
204
|
+
result.putString("faceFeature", "")
|
|
205
|
+
} else {
|
|
206
|
+
result.putInt("code", 1)
|
|
207
|
+
result.putString("msg", "Face Feature exist")
|
|
208
|
+
result.putString("faceFeature", faceFeature)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
result.putString("faceID", faceID)
|
|
212
|
+
result.putDouble("similarity", 0.0)
|
|
213
|
+
result.putDouble("liveness", 0.0)
|
|
214
|
+
result.putString("faceBase64", "")
|
|
215
|
+
callback.invoke(result)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 5. 同步人脸特征信息
|
|
220
|
+
*/
|
|
221
|
+
@ReactMethod
|
|
222
|
+
fun insertFaceFeature(faceID: String, faceFeature: String, callback: Callback) {
|
|
223
|
+
val context = reactApplicationContext.applicationContext
|
|
224
|
+
FaceSDKConfig.init(context)
|
|
225
|
+
|
|
226
|
+
val result = Arguments.createMap()
|
|
227
|
+
|
|
228
|
+
if (TextUtils.isEmpty(faceFeature)) {
|
|
229
|
+
result.putInt("code", 0)
|
|
230
|
+
result.putString("msg", "Face Feature not exist")
|
|
231
|
+
} else if (faceFeature.length != 1024) {
|
|
232
|
+
result.putInt("code", 0)
|
|
233
|
+
result.putString("msg", "Face Feature length should be 1024")
|
|
234
|
+
} else {
|
|
235
|
+
MMKV.defaultMMKV().encode(faceID, faceFeature)
|
|
236
|
+
result.putInt("code", 1)
|
|
237
|
+
result.putString("msg", "insert Face success")
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
result.putString("faceID", faceID)
|
|
241
|
+
result.putDouble("similarity", 0.0)
|
|
242
|
+
result.putDouble("liveness", 0.0)
|
|
243
|
+
result.putString("faceFeature", "")
|
|
244
|
+
result.putString("faceBase64", "")
|
|
245
|
+
callback.invoke(result)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 6. 通过图片录入人脸信息
|
|
250
|
+
*/
|
|
251
|
+
@ReactMethod
|
|
252
|
+
fun addFaceBySDKImage(faceID: String, base64FaceImage: String, callback: Callback) {
|
|
253
|
+
val activity = reactApplicationContext.currentActivity ?: return
|
|
254
|
+
FaceSDKConfig.init(activity)
|
|
255
|
+
|
|
256
|
+
Image2FaceFeature.getInstance(activity)
|
|
257
|
+
.getFaceFeatureByBase64(base64FaceImage, faceID, object : Image2FaceFeature.Callback {
|
|
258
|
+
override fun onFailed(msg: String) {
|
|
259
|
+
val result = Arguments.createMap()
|
|
260
|
+
result.putInt("code", 0)
|
|
261
|
+
result.putString("msg", msg)
|
|
262
|
+
result.putString("faceID", faceID)
|
|
263
|
+
result.putDouble("similarity", 0.0)
|
|
264
|
+
result.putDouble("liveness", 0.0)
|
|
265
|
+
result.putString("faceFeature", "")
|
|
266
|
+
result.putString("faceBase64", "")
|
|
267
|
+
callback.invoke(result)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
override fun onSuccess(bitmap: Bitmap, returnFaceID: String, faceFeature: String) {
|
|
271
|
+
com.ai.face.core.engine.FaceAISDKEngine.getInstance(activity)
|
|
272
|
+
.saveCroppedFaceImage(bitmap, FaceSDKConfig.CACHE_BASE_FACE_DIR, faceID)
|
|
273
|
+
MMKV.defaultMMKV().encode(faceID, faceFeature)
|
|
274
|
+
|
|
275
|
+
val result = Arguments.createMap()
|
|
276
|
+
result.putInt("code", 1)
|
|
277
|
+
result.putString("msg", "getFaceFeature Success")
|
|
278
|
+
result.putString("faceID", faceID)
|
|
279
|
+
result.putDouble("similarity", 0.0)
|
|
280
|
+
result.putDouble("liveness", 0.0)
|
|
281
|
+
result.putString("faceFeature", faceFeature)
|
|
282
|
+
result.putString("faceBase64", "")
|
|
283
|
+
callback.invoke(result)
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* 7. 删除人脸特征信息
|
|
290
|
+
*/
|
|
291
|
+
@ReactMethod
|
|
292
|
+
fun deleteFaceFeature(faceID: String, callback: Callback) {
|
|
293
|
+
val context = reactApplicationContext.applicationContext
|
|
294
|
+
FaceSDKConfig.init(context)
|
|
295
|
+
|
|
296
|
+
MMKV.defaultMMKV().removeValueForKey(faceID)
|
|
297
|
+
Image2FaceFeature.getInstance(context)
|
|
298
|
+
.deleteFaceImage(FaceSDKConfig.CACHE_BASE_FACE_DIR + faceID)
|
|
299
|
+
|
|
300
|
+
val result = Arguments.createMap()
|
|
301
|
+
result.putInt("code", 1)
|
|
302
|
+
result.putString("msg", "Delete Success")
|
|
303
|
+
result.putString("faceID", faceID)
|
|
304
|
+
result.putDouble("similarity", 0.0)
|
|
305
|
+
result.putDouble("liveness", 0.0)
|
|
306
|
+
result.putString("faceFeature", "")
|
|
307
|
+
result.putString("faceBase64", "")
|
|
308
|
+
callback.invoke(result)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
override fun onActivityResult(
|
|
312
|
+
activity: Activity,
|
|
313
|
+
requestCode: Int,
|
|
314
|
+
resultCode: Int,
|
|
315
|
+
data: Intent?
|
|
316
|
+
) {
|
|
317
|
+
if (mCallback == null) return
|
|
318
|
+
if (requestCode != REQ_CODE_ADD_FACE && requestCode != REQ_CODE_VERIFY && requestCode != REQ_CODE_LIVENESS) {
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
val result = Arguments.createMap()
|
|
323
|
+
result.putString("faceID", mCurrentFaceID)
|
|
324
|
+
result.putDouble("similarity", 0.0)
|
|
325
|
+
result.putDouble("liveness", 0.0)
|
|
326
|
+
result.putString("faceFeature", "")
|
|
327
|
+
result.putString("faceBase64", "")
|
|
328
|
+
|
|
329
|
+
if (data != null) {
|
|
330
|
+
result.putInt("code", data.getIntExtra("code", 0))
|
|
331
|
+
result.putString("msg", data.getStringExtra("msg") ?: "操作完成")
|
|
332
|
+
|
|
333
|
+
when (requestCode) {
|
|
334
|
+
REQ_CODE_ADD_FACE -> {
|
|
335
|
+
result.putString("faceFeature", data.getStringExtra("faceFeature") ?: "")
|
|
336
|
+
if (data.getIntExtra("code", 0) != 0) {
|
|
337
|
+
val base64 = BitmapUtils.bitmapToBase64(
|
|
338
|
+
FaceSDKConfig.CACHE_BASE_FACE_DIR + mCurrentFaceID
|
|
339
|
+
)
|
|
340
|
+
result.putString("faceBase64", base64 ?: "")
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
REQ_CODE_VERIFY -> {
|
|
344
|
+
result.putDouble(
|
|
345
|
+
"similarity",
|
|
346
|
+
data.getFloatExtra("similarity", 0f).toDouble()
|
|
347
|
+
)
|
|
348
|
+
result.putDouble(
|
|
349
|
+
"liveness",
|
|
350
|
+
data.getFloatExtra("livenessValue", 0f).toDouble()
|
|
351
|
+
)
|
|
352
|
+
if (data.getIntExtra("code", 0) == 1) {
|
|
353
|
+
val base64 = BitmapUtils.bitmapToBase64(
|
|
354
|
+
FaceSDKConfig.CACHE_FACE_LOG_DIR + "verifyBitmap"
|
|
355
|
+
)
|
|
356
|
+
result.putString("faceBase64", base64 ?: "")
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
REQ_CODE_LIVENESS -> {
|
|
360
|
+
result.putDouble(
|
|
361
|
+
"liveness",
|
|
362
|
+
data.getFloatExtra("livenessValue", 0f).toDouble()
|
|
363
|
+
)
|
|
364
|
+
if (data.getIntExtra("code", 0) == 10) {
|
|
365
|
+
val base64 = BitmapUtils.bitmapToBase64(
|
|
366
|
+
FaceSDKConfig.CACHE_FACE_LOG_DIR + "liveBitmap"
|
|
367
|
+
)
|
|
368
|
+
result.putString("faceBase64", base64 ?: "")
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
result.putInt("code", 0)
|
|
374
|
+
result.putString("msg", "用户取消/中断操作")
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
mCallback?.invoke(result)
|
|
378
|
+
mCallback = null
|
|
379
|
+
mCurrentFaceID = ""
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
override fun onNewIntent(intent: Intent) {}
|
|
383
|
+
}
|