@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
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import UIKit
|
|
3
|
+
import FaceAISDK_Core
|
|
4
|
+
import AVFoundation
|
|
5
|
+
import Combine
|
|
6
|
+
|
|
7
|
+
@objcMembers
|
|
8
|
+
public class FaceSDKSwiftManager: NSObject {
|
|
9
|
+
|
|
10
|
+
// MARK: - 相机权限检查
|
|
11
|
+
private static func checkCameraPermission(completion: @escaping (Bool) -> Void) {
|
|
12
|
+
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
|
13
|
+
switch status {
|
|
14
|
+
case .authorized:
|
|
15
|
+
completion(true)
|
|
16
|
+
case .notDetermined:
|
|
17
|
+
AVCaptureDevice.requestAccess(for: .video) { granted in
|
|
18
|
+
DispatchQueue.main.async {
|
|
19
|
+
completion(granted)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
default:
|
|
23
|
+
completion(false)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// MARK: - 特征值管理 (核心修复点)
|
|
28
|
+
public static func getiOSFaceFeature(_ faceID: String) -> String {
|
|
29
|
+
return UserDefaults.standard.string(forKey: faceID) ?? ""
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
// MARK: - Base64 提取人脸特征 (支持插件/外部调用)
|
|
34
|
+
public static func addFaceByBase64(_ faceID: String,
|
|
35
|
+
_ base64Str: String,
|
|
36
|
+
_ callback: @escaping (NSNumber, String, String) -> Void) {
|
|
37
|
+
|
|
38
|
+
// 1. 预处理 Base64 字符串(可以在后台线程做)
|
|
39
|
+
var cleanBase64 = base64Str
|
|
40
|
+
if let idx = cleanBase64.range(of: "base64,")?.upperBound {
|
|
41
|
+
cleanBase64 = String(cleanBase64[idx...])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
guard let data = Data(base64Encoded: cleanBase64, options: .ignoreUnknownCharacters),
|
|
45
|
+
let image = UIImage(data: data) else {
|
|
46
|
+
callback(0, "", FaceSDKLocalizer.text("Base64 image parse failed"))
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. 切换到主线程操作 @MainActor 隔离的 Model
|
|
51
|
+
DispatchQueue.main.async {
|
|
52
|
+
let model = AddFaceByImageModel()
|
|
53
|
+
let feature = model.getFaceFeature(faceUIImage: image)
|
|
54
|
+
if !feature.isEmpty {
|
|
55
|
+
UserDefaults.standard.set(feature, forKey: faceID)
|
|
56
|
+
UserDefaults.standard.synchronize()
|
|
57
|
+
// 回调成功
|
|
58
|
+
callback(
|
|
59
|
+
1,
|
|
60
|
+
feature,
|
|
61
|
+
String(
|
|
62
|
+
format: FaceSDKLocalizer.text("Face feature extracted successfully, length: %ld"),
|
|
63
|
+
feature.count
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
} else {
|
|
67
|
+
// 提取失败
|
|
68
|
+
callback(0, "", FaceSDKLocalizer.text("Unable to extract face feature, ensure image is clear and contains a face"))
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
// 插入人脸特征值:增加长度判断拦截
|
|
76
|
+
public static func insertFaceFeature(_ faceID: String,
|
|
77
|
+
_ feature: String,
|
|
78
|
+
_ callback: @escaping (NSNumber,String) -> Void) {
|
|
79
|
+
|
|
80
|
+
// 1. 拦截:判断字符串是否为空,或者长度是否小于 1024
|
|
81
|
+
// 特征值通常是加密后的超长字符串,如果太短说明数据不完整
|
|
82
|
+
if feature.isEmpty || feature.count < 1024 {
|
|
83
|
+
callback(
|
|
84
|
+
NSNumber(value: 0),
|
|
85
|
+
String(
|
|
86
|
+
format: FaceSDKLocalizer.text("Feature length only %ld"),
|
|
87
|
+
feature.count
|
|
88
|
+
)
|
|
89
|
+
) // 返回 0 表示失败
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 2. 校验通过,执行存储
|
|
94
|
+
UserDefaults.standard.set(feature, forKey: faceID)
|
|
95
|
+
|
|
96
|
+
print("【FaceSDK】人脸特征值插入成功,ID: \(faceID)")
|
|
97
|
+
callback(NSNumber(value: 1),"\(faceID) \(FaceSDKLocalizer.text("Face sync succeeded"))") // 返回 1 表示成功
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
// MARK: - 呼出相机录入人脸
|
|
102
|
+
public static func showAddFaceByCamera(_ faceID: String,
|
|
103
|
+
_ performanceMode: NSNumber,
|
|
104
|
+
_ needConfirm: Bool,
|
|
105
|
+
_ callback: @escaping (NSNumber, String) -> Void) {
|
|
106
|
+
DispatchQueue.main.async {
|
|
107
|
+
checkCameraPermission { granted in
|
|
108
|
+
guard granted else {
|
|
109
|
+
callback(NSNumber(value: 0), "")
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
guard let topVC = self.getTopViewController() else {
|
|
113
|
+
callback(NSNumber(value: 0), "")
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
var sdkView = AddFaceByCamera(
|
|
118
|
+
faceID: faceID,
|
|
119
|
+
addFacePerformanceMode: performanceMode.intValue,
|
|
120
|
+
needShowConfirmDialog: needConfirm,
|
|
121
|
+
onDismiss: { [weak topVC] (resultCode: Int, feature: String) in
|
|
122
|
+
|
|
123
|
+
let safeCode = NSNumber(value: resultCode)
|
|
124
|
+
|
|
125
|
+
DispatchQueue.main.async {
|
|
126
|
+
ScreenBrightnessHelper.shared.restoreBrightness()
|
|
127
|
+
topVC?.dismiss(animated: true) {
|
|
128
|
+
callback(safeCode, feature)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
sdkView.autoControlBrightness = false
|
|
134
|
+
|
|
135
|
+
let hostingController = UIHostingController(rootView: sdkView)
|
|
136
|
+
hostingController.modalPresentationStyle = .fullScreen
|
|
137
|
+
topVC.present(hostingController, animated: true)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// MARK: - 1:1 人脸识别
|
|
143
|
+
public static func showFaceVerify(_ faceID: String,
|
|
144
|
+
_ threshold: NSNumber,
|
|
145
|
+
_ livenessType: NSNumber,
|
|
146
|
+
_ motionLivenessTypes: String,
|
|
147
|
+
_ motionLivenessTimeOut : NSNumber,
|
|
148
|
+
_ motionLivenessSteps : NSNumber,
|
|
149
|
+
_ callback: @escaping (NSNumber, NSNumber, NSNumber) -> Void) {
|
|
150
|
+
DispatchQueue.main.async {
|
|
151
|
+
checkCameraPermission { granted in
|
|
152
|
+
guard granted else {
|
|
153
|
+
callback(NSNumber(value: 0), NSNumber(value: 0), NSNumber(value: 0))
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
guard let topVC = self.getTopViewController() else {
|
|
157
|
+
callback(NSNumber(value: 0), NSNumber(value: 0), NSNumber(value: 0))
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
ScreenBrightnessHelper.shared.maximizeBrightness()
|
|
161
|
+
|
|
162
|
+
var sdkView = VerifyFaceView(
|
|
163
|
+
faceID: faceID,
|
|
164
|
+
threshold: threshold.floatValue,
|
|
165
|
+
livenessType: livenessType.intValue,
|
|
166
|
+
motionLiveness: motionLivenessTypes,
|
|
167
|
+
motionLivenessTimeOut: motionLivenessTimeOut.intValue,
|
|
168
|
+
motionLivenessSteps: motionLivenessSteps.intValue,
|
|
169
|
+
onDismiss: { [weak topVC] (resultCode: Int, similarity: Float, liveness: Float) in
|
|
170
|
+
DispatchQueue.main.async {
|
|
171
|
+
ScreenBrightnessHelper.shared.restoreBrightness()
|
|
172
|
+
topVC?.dismiss(animated: true) {
|
|
173
|
+
callback(NSNumber(value: resultCode), NSNumber(value: similarity), NSNumber(value: liveness))
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
sdkView.autoControlBrightness = false
|
|
179
|
+
|
|
180
|
+
let hostingController = UIHostingController(rootView: sdkView)
|
|
181
|
+
hostingController.modalPresentationStyle = .fullScreen
|
|
182
|
+
topVC.present(hostingController, animated: true)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// MARK: - 活体检测
|
|
188
|
+
public static func showLivenessVerify(_ livenessType: NSNumber,
|
|
189
|
+
_ motionLivenessTypes: String,
|
|
190
|
+
_ motionLivenessTimeOut : NSNumber,
|
|
191
|
+
_ motionLivenessSteps : NSNumber,
|
|
192
|
+
_ showResultTips: Bool,
|
|
193
|
+
_ callback: @escaping (NSNumber, NSNumber) -> Void) {
|
|
194
|
+
DispatchQueue.main.async {
|
|
195
|
+
checkCameraPermission { granted in
|
|
196
|
+
guard granted else {
|
|
197
|
+
callback(NSNumber(value: 0), NSNumber(value: 0))
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
guard let topVC = self.getTopViewController() else {
|
|
201
|
+
callback(NSNumber(value: 0), NSNumber(value: 0))
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
ScreenBrightnessHelper.shared.maximizeBrightness()
|
|
205
|
+
|
|
206
|
+
var sdkView = LivenessDetectView(
|
|
207
|
+
livenessType: livenessType.intValue,
|
|
208
|
+
motionLiveness: motionLivenessTypes,
|
|
209
|
+
motionLivenessTimeOut: motionLivenessTimeOut.intValue,
|
|
210
|
+
motionLivenessSteps: motionLivenessSteps.intValue,
|
|
211
|
+
showResultTips: showResultTips,
|
|
212
|
+
onDismiss: { [weak topVC] (resultCode: Int, liveness: Float) in
|
|
213
|
+
DispatchQueue.main.async {
|
|
214
|
+
ScreenBrightnessHelper.shared.restoreBrightness()
|
|
215
|
+
topVC?.dismiss(animated: true) {
|
|
216
|
+
callback(NSNumber(value: resultCode), NSNumber(value: liveness))
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
sdkView.autoControlBrightness = false
|
|
222
|
+
|
|
223
|
+
let hostingController = UIHostingController(rootView: sdkView)
|
|
224
|
+
hostingController.modalPresentationStyle = .fullScreen
|
|
225
|
+
topVC.present(hostingController, animated: true)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 临时操作的图片转Base64 编码
|
|
231
|
+
public static func getFaceImageBase64(_ faceName: String) -> String {
|
|
232
|
+
guard let faceImageBase64 = FaceImageManager.faceImageToBase64(fileName: faceName) else {
|
|
233
|
+
print("❌ [Swift] getFaceImageBase64 failed")
|
|
234
|
+
return ""
|
|
235
|
+
}
|
|
236
|
+
return faceImageBase64
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
public static func isFaceFeatureExist(_ faceID: String, _ callback: @escaping (NSNumber,String) -> Void) {
|
|
242
|
+
let featureString = UserDefaults.standard.string(forKey: faceID)
|
|
243
|
+
// 2. 只有当字符串不为 nil 且长度正好为 1024 时,才判定为 true
|
|
244
|
+
let exists = (featureString?.count == 1024)
|
|
245
|
+
callback(
|
|
246
|
+
NSNumber(value: exists ? 1 : 0),
|
|
247
|
+
String(
|
|
248
|
+
format: FaceSDKLocalizer.text("Feature length=%ld"),
|
|
249
|
+
featureString?.count ?? 0
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
public static func deleteFaceFeature(_ faceID: String) {
|
|
256
|
+
UserDefaults.standard.removeObject(forKey: faceID)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
// MARK: - 辅助方法
|
|
262
|
+
private static func getTopViewController() -> UIViewController? {
|
|
263
|
+
let windowScene = UIApplication.shared.connectedScenes
|
|
264
|
+
.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
|
265
|
+
|
|
266
|
+
guard let keyWindow = windowScene?.windows.first(where: { $0.isKeyWindow }),
|
|
267
|
+
let rootVC = keyWindow.rootViewController else {
|
|
268
|
+
return nil
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
var topController = rootVC
|
|
272
|
+
while let presentedViewController = topController.presentedViewController {
|
|
273
|
+
topController = presentedViewController
|
|
274
|
+
}
|
|
275
|
+
return topController
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"About us" = "About us";
|
|
2
|
+
"Add Face By Camera" = "Add Face By Camera";
|
|
3
|
+
"Add Face From Album" = "Add Face From Album";
|
|
4
|
+
"Confirm" = "Confirm";
|
|
5
|
+
"Confirm Add Face" = "Confirm Add Face";
|
|
6
|
+
"Ensure face is clear" = "Ensure face is clear";
|
|
7
|
+
"Face Verify & Liveness" = "Face Verify & Liveness";
|
|
8
|
+
"Face_Tips_Code_-1" = "FaceSDK Version Too Old";
|
|
9
|
+
"Face_Tips_Code_(code)" = "error";
|
|
10
|
+
"Face_Tips_Code_0" = "Keep Face Frontal";
|
|
11
|
+
"Face_Tips_Code_1" = "No Face Detected";
|
|
12
|
+
"Face_Tips_Code_2" = "Detect Too Many Face";
|
|
13
|
+
"Face_Tips_Code_3" = "Please Come Closer";
|
|
14
|
+
"Face_Tips_Code_4" = "Keep Face Frontal Visible";
|
|
15
|
+
"Face_Tips_Code_5" = "Do not Close Eye";
|
|
16
|
+
"Face_Tips_Code_6" = "Turn Face Right";
|
|
17
|
+
"Face_Tips_Code_7" = "Turn Face Left";
|
|
18
|
+
"Face_Tips_Code_8" = "Do not Tile Face";
|
|
19
|
+
"Face_Tips_Code_9" = "Turn Face Up";
|
|
20
|
+
"Face_Tips_Code_10" = "Turn Face Down";
|
|
21
|
+
"Face_Tips_Code_11" = "Confirm Add Face";
|
|
22
|
+
"Face_Tips_Code_12" = " ";
|
|
23
|
+
"Face_Tips_Code_13" = "Too Small Face";
|
|
24
|
+
"Face_Tips_Code_31" = "Face Image Not Found ";
|
|
25
|
+
"Face_Tips_Code_32" = "Face Image Dispose Failed";
|
|
26
|
+
"Face_Tips_Code_33" = "SDK Error";
|
|
27
|
+
"Face_Tips_Code_34" = "Motion Liveness Complete";
|
|
28
|
+
"Face_Tips_Code_35" = "Motion Liveness Timeout";
|
|
29
|
+
"Face_Tips_Code_38" = "No Face Detected Multi Time";
|
|
30
|
+
"Face_Tips_Code_41" = "Open Mouse";
|
|
31
|
+
"Face_Tips_Code_42" = "Please Smile";
|
|
32
|
+
"Face_Tips_Code_43" = "Please Blink";
|
|
33
|
+
"Face_Tips_Code_44" = "Please Shake Head";
|
|
34
|
+
"Face_Tips_Code_45" = "Please Nod Head";
|
|
35
|
+
"Face_Tips_Code_50" = "Start Color Flash";
|
|
36
|
+
"Face_Tips_Code_51" = "Color Flash Success";
|
|
37
|
+
"Face_Tips_Code_52" = "Color Flash Failed";
|
|
38
|
+
"Face_Tips_Code_53" = "Detection failed: Too bright.Please avoid direct light as below and use in a softly lit indoor environment";
|
|
39
|
+
"Face_Tips_Code_58" = "Excessive light, avoid strong light exposure.";
|
|
40
|
+
"Face_Tips_Code_60" = "Liveness check failed. Please ensure soft lighting conditions.";
|
|
41
|
+
"Face_Tips_Code_61" = "Face Liveness Success";
|
|
42
|
+
"Face_Tips_Code_62" = "Face Verify Success";
|
|
43
|
+
"Face_Tips_Code_63" = "Face Verify Failed";
|
|
44
|
+
"I Know" = "I Know";
|
|
45
|
+
"Is Face Feature Exist" = "Is Face Feature Exist";
|
|
46
|
+
"ONLY Liveness Detection" = "ONLY Liveness Detection";
|
|
47
|
+
"Retry" = "Retry";
|
|
48
|
+
"Save Face Feature" = "Save Face Feature";
|
|
49
|
+
"Select from album" = "Select from album";
|
|
50
|
+
"Select Image" = "Select Image";
|
|
51
|
+
"Verify Two Face Similarity" = "Verify Two Face Similarity";
|
|
Binary file
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"About us" = "关于我们";
|
|
2
|
+
"Add Face By Camera" = "SDK相机添加人脸";
|
|
3
|
+
"Add Face From Album" = "从相册添加一人脸";
|
|
4
|
+
"Confirm" = "确认";
|
|
5
|
+
"Confirm Add Face" = "确认录入此人脸图?";
|
|
6
|
+
"Ensure face is clear" = "请录入无遮挡正脸清晰图";
|
|
7
|
+
"Face Verify & Liveness" = "人脸识别+活体检测";
|
|
8
|
+
"Face_Tips_Code_-1" = "FaceSDK Version Too Old";
|
|
9
|
+
"Face_Tips_Code_(code)" = "error";
|
|
10
|
+
"Face_Tips_Code_0" = "请正对摄像头";
|
|
11
|
+
"Face_Tips_Code_1" = "未检测到人脸";
|
|
12
|
+
"Face_Tips_Code_2" = "检测到多个人脸";
|
|
13
|
+
"Face_Tips_Code_3" = "请靠近一点";
|
|
14
|
+
"Face_Tips_Code_4" = "保持正脸不要晃动";
|
|
15
|
+
"Face_Tips_Code_5" = "不要闭眼睛";
|
|
16
|
+
"Face_Tips_Code_6" = "向右摆正头部";
|
|
17
|
+
"Face_Tips_Code_7" = "向左摆正头部";
|
|
18
|
+
"Face_Tips_Code_8" = "不要歪头";
|
|
19
|
+
"Face_Tips_Code_9" = "向上摆正头部";
|
|
20
|
+
"Face_Tips_Code_10" = "向下摆正头部";
|
|
21
|
+
"Face_Tips_Code_11" = "人脸采集确认";
|
|
22
|
+
"Face_Tips_Code_12" = " ";
|
|
23
|
+
"Face_Tips_Code_13" = "人脸太小了";
|
|
24
|
+
"Face_Tips_Code_31" = "没有找到对应的人脸底片";
|
|
25
|
+
"Face_Tips_Code_32" = "人脸底片处理失败";
|
|
26
|
+
"Face_Tips_Code_33" = "SDK 内部错误";
|
|
27
|
+
"Face_Tips_Code_34" = "动作活体检测完成";
|
|
28
|
+
"Face_Tips_Code_35" = "动作活体检验超时";
|
|
29
|
+
"Face_Tips_Code_38" = "多次切换人脸或检测失败";
|
|
30
|
+
"Face_Tips_Code_41" = "请张张嘴";
|
|
31
|
+
"Face_Tips_Code_42" = "请微笑";
|
|
32
|
+
"Face_Tips_Code_43" = "请眨眼";
|
|
33
|
+
"Face_Tips_Code_44" = "请摇头";
|
|
34
|
+
"Face_Tips_Code_45" = "请点头";
|
|
35
|
+
"Face_Tips_Code_50" = "请靠近,屏幕即将闪烁";
|
|
36
|
+
"Face_Tips_Code_51" = "炫彩活体成功";
|
|
37
|
+
"Face_Tips_Code_52" = "炫彩活体失败";
|
|
38
|
+
"Face_Tips_Code_53" = "炫彩活体检测失败,光线亮度过高;如图遮挡高亮光源或室内光线柔和环境使用";
|
|
39
|
+
"Face_Tips_Code_58" = "光线过强,避免强光照射";
|
|
40
|
+
"Face_Tips_Code_60" = "活体检测失败,请确认光线环境柔和";
|
|
41
|
+
"Face_Tips_Code_61" = "活体检测完成";
|
|
42
|
+
"Face_Tips_Code_62" = "人脸识别成功";
|
|
43
|
+
"Face_Tips_Code_63" = "人脸识别失败";
|
|
44
|
+
"I Know" = "知道了";
|
|
45
|
+
"Is Face Feature Exist" = "人脸特征值是否存在";
|
|
46
|
+
"ONLY Liveness Detection" = "仅仅活体检测";
|
|
47
|
+
"Retry" = "重试";
|
|
48
|
+
"Save Face Feature" = "保存人脸特征";
|
|
49
|
+
"Select from album" = "从相册选择";
|
|
50
|
+
"Select Image" = "选择图片";
|
|
51
|
+
"Verify Two Face Similarity" = "静态人脸相似度对比";
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AddFaceBySDKCameraOptions, FaceResult, FaceVerifyOptions, LivenessVerifyOptions } from './types';
|
|
2
|
+
export declare const FACE_AI_STATUS_CODE_MAP: Record<number, string>;
|
|
3
|
+
export declare function isFaceAIModuleAvailable(): boolean;
|
|
4
|
+
export declare function addFaceBySDKCamera(faceID: string, options?: AddFaceBySDKCameraOptions): Promise<FaceResult>;
|
|
5
|
+
export declare function faceVerify(faceID: string, options?: FaceVerifyOptions): Promise<FaceResult>;
|
|
6
|
+
export declare function livenessVerify(options?: LivenessVerifyOptions): Promise<FaceResult>;
|
|
7
|
+
export declare function getFaceFeature(faceID: string): Promise<FaceResult>;
|
|
8
|
+
export declare function insertFaceFeature(faceID: string, faceFeature: string): Promise<FaceResult>;
|
|
9
|
+
export declare function addFaceByImage(faceID: string, base64Image: string): Promise<FaceResult>;
|
|
10
|
+
export declare function deleteFaceFeature(faceID: string): Promise<FaceResult>;
|
|
11
|
+
export type { AddFaceBySDKCameraOptions, FaceResult, FaceVerifyOptions, LivenessVerifyOptions, } from './types';
|
|
12
|
+
declare const _default: {
|
|
13
|
+
addFaceBySDKCamera: typeof addFaceBySDKCamera;
|
|
14
|
+
faceVerify: typeof faceVerify;
|
|
15
|
+
livenessVerify: typeof livenessVerify;
|
|
16
|
+
getFaceFeature: typeof getFaceFeature;
|
|
17
|
+
insertFaceFeature: typeof insertFaceFeature;
|
|
18
|
+
addFaceByImage: typeof addFaceByImage;
|
|
19
|
+
deleteFaceFeature: typeof deleteFaceFeature;
|
|
20
|
+
isFaceAIModuleAvailable: typeof isFaceAIModuleAvailable;
|
|
21
|
+
};
|
|
22
|
+
export default _default;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FACE_AI_STATUS_CODE_MAP = void 0;
|
|
4
|
+
exports.isFaceAIModuleAvailable = isFaceAIModuleAvailable;
|
|
5
|
+
exports.addFaceBySDKCamera = addFaceBySDKCamera;
|
|
6
|
+
exports.faceVerify = faceVerify;
|
|
7
|
+
exports.livenessVerify = livenessVerify;
|
|
8
|
+
exports.getFaceFeature = getFaceFeature;
|
|
9
|
+
exports.insertFaceFeature = insertFaceFeature;
|
|
10
|
+
exports.addFaceByImage = addFaceByImage;
|
|
11
|
+
exports.deleteFaceFeature = deleteFaceFeature;
|
|
12
|
+
const react_native_1 = require("react-native");
|
|
13
|
+
const MODULE_NAME = 'FaceRNModule';
|
|
14
|
+
const LINKING_ERROR = `The native module \`${MODULE_NAME}\` is not linked. Make sure that:\n\n` +
|
|
15
|
+
react_native_1.Platform.select({
|
|
16
|
+
ios: "- you ran 'pod install' in the iOS project\n",
|
|
17
|
+
default: '',
|
|
18
|
+
}) +
|
|
19
|
+
'- you rebuilt the app after installing the package\n' +
|
|
20
|
+
'- you are not running inside a plain Jest/Node environment without mocks';
|
|
21
|
+
const nativeModule = react_native_1.NativeModules[MODULE_NAME];
|
|
22
|
+
function getNativeModule() {
|
|
23
|
+
if (!nativeModule) {
|
|
24
|
+
throw new Error(LINKING_ERROR);
|
|
25
|
+
}
|
|
26
|
+
return nativeModule;
|
|
27
|
+
}
|
|
28
|
+
function normalizeResult(result) {
|
|
29
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
30
|
+
return {
|
|
31
|
+
code: (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : 0,
|
|
32
|
+
msg: (_b = result === null || result === void 0 ? void 0 : result.msg) !== null && _b !== void 0 ? _b : '',
|
|
33
|
+
faceID: (_c = result === null || result === void 0 ? void 0 : result.faceID) !== null && _c !== void 0 ? _c : '',
|
|
34
|
+
similarity: (_d = result === null || result === void 0 ? void 0 : result.similarity) !== null && _d !== void 0 ? _d : 0,
|
|
35
|
+
liveness: (_e = result === null || result === void 0 ? void 0 : result.liveness) !== null && _e !== void 0 ? _e : 0,
|
|
36
|
+
faceFeature: (_f = result === null || result === void 0 ? void 0 : result.faceFeature) !== null && _f !== void 0 ? _f : '',
|
|
37
|
+
faceBase64: (_g = result === null || result === void 0 ? void 0 : result.faceBase64) !== null && _g !== void 0 ? _g : '',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function invokeWithPromise(call) {
|
|
41
|
+
return new Promise(resolve => {
|
|
42
|
+
call(result => resolve(normalizeResult(result)));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
exports.FACE_AI_STATUS_CODE_MAP = {
|
|
46
|
+
[-1]: '相机权限被拒绝',
|
|
47
|
+
0: '初始化状态/用户取消',
|
|
48
|
+
1: '人脸识别或录入成功',
|
|
49
|
+
2: '人脸识别失败,相似度低于阈值',
|
|
50
|
+
3: '动作活体检测成功',
|
|
51
|
+
4: '动作活体检测超时',
|
|
52
|
+
5: '多次未检测到人脸',
|
|
53
|
+
6: '本地不存在对应的人脸特征',
|
|
54
|
+
7: '炫彩活体检测成功',
|
|
55
|
+
8: '炫彩活体检测失败',
|
|
56
|
+
9: '炫彩活体检测失败,环境过亮',
|
|
57
|
+
10: '活体检测流程完成',
|
|
58
|
+
11: '静默活体检测失败',
|
|
59
|
+
12: '未录入对应人脸',
|
|
60
|
+
13: '检测到多人脸',
|
|
61
|
+
};
|
|
62
|
+
function isFaceAIModuleAvailable() {
|
|
63
|
+
return Boolean(nativeModule);
|
|
64
|
+
}
|
|
65
|
+
function addFaceBySDKCamera(faceID, options = {}) {
|
|
66
|
+
const { mode = 1, showConfirm = true } = options;
|
|
67
|
+
return invokeWithPromise(callback => {
|
|
68
|
+
getNativeModule().addFaceBySDKCamera(faceID, mode, showConfirm, callback);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function faceVerify(faceID, options = {}) {
|
|
72
|
+
const { threshold = 0.83, livenessType = 1, motionTypes = '1,2,3,4,5', timeout = 7, steps = 2, allowMultiFaces = true, } = options;
|
|
73
|
+
return invokeWithPromise(callback => {
|
|
74
|
+
getNativeModule().faceVerify(faceID, threshold, livenessType, motionTypes, timeout, steps, allowMultiFaces, callback);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function livenessVerify(options = {}) {
|
|
78
|
+
const { livenessType = 2, motionTypes = '1,2,3,4,5', timeout = 7, steps = 2, allowMultiFaces = true, showResultTips = false, } = options;
|
|
79
|
+
return invokeWithPromise(callback => {
|
|
80
|
+
getNativeModule().livenessVerify(livenessType, motionTypes, timeout, steps, allowMultiFaces, showResultTips, callback);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function getFaceFeature(faceID) {
|
|
84
|
+
return invokeWithPromise(callback => {
|
|
85
|
+
getNativeModule().getFaceFeature(faceID, callback);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function insertFaceFeature(faceID, faceFeature) {
|
|
89
|
+
return invokeWithPromise(callback => {
|
|
90
|
+
getNativeModule().insertFaceFeature(faceID, faceFeature, callback);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function addFaceByImage(faceID, base64Image) {
|
|
94
|
+
return invokeWithPromise(callback => {
|
|
95
|
+
getNativeModule().addFaceBySDKImage(faceID, base64Image, callback);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function deleteFaceFeature(faceID) {
|
|
99
|
+
return invokeWithPromise(callback => {
|
|
100
|
+
getNativeModule().deleteFaceFeature(faceID, callback);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
exports.default = {
|
|
104
|
+
addFaceBySDKCamera,
|
|
105
|
+
faceVerify,
|
|
106
|
+
livenessVerify,
|
|
107
|
+
getFaceFeature,
|
|
108
|
+
insertFaceFeature,
|
|
109
|
+
addFaceByImage,
|
|
110
|
+
deleteFaceFeature,
|
|
111
|
+
isFaceAIModuleAvailable,
|
|
112
|
+
};
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type FaceResultCode = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13;
|
|
2
|
+
export interface FaceResult {
|
|
3
|
+
code: FaceResultCode | number;
|
|
4
|
+
msg: string;
|
|
5
|
+
faceID: string;
|
|
6
|
+
similarity: number;
|
|
7
|
+
liveness: number;
|
|
8
|
+
faceFeature: string;
|
|
9
|
+
faceBase64: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AddFaceBySDKCameraOptions {
|
|
12
|
+
mode?: 1 | 2;
|
|
13
|
+
showConfirm?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface FaceVerifyOptions {
|
|
16
|
+
threshold?: number;
|
|
17
|
+
livenessType?: 1 | 2 | 3 | 4;
|
|
18
|
+
motionTypes?: string;
|
|
19
|
+
timeout?: number;
|
|
20
|
+
steps?: number;
|
|
21
|
+
allowMultiFaces?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface LivenessVerifyOptions {
|
|
24
|
+
livenessType?: 1 | 2 | 3 | 4;
|
|
25
|
+
motionTypes?: string;
|
|
26
|
+
timeout?: number;
|
|
27
|
+
steps?: number;
|
|
28
|
+
allowMultiFaces?: boolean;
|
|
29
|
+
showResultTips?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface FaceNativeModule {
|
|
32
|
+
addFaceBySDKCamera(faceID: string, mode: number, showConfirm: boolean, callback: (result: FaceResult) => void): void;
|
|
33
|
+
faceVerify(faceID: string, threshold: number, livenessType: number, motionTypes: string, timeout: number, steps: number, allowMultiFaces: boolean, callback: (result: FaceResult) => void): void;
|
|
34
|
+
livenessVerify(livenessType: number, motionTypes: string, timeout: number, steps: number, allowMultiFaces: boolean, showResultTips: boolean, callback: (result: FaceResult) => void): void;
|
|
35
|
+
getFaceFeature(faceID: string, callback: (result: FaceResult) => void): void;
|
|
36
|
+
insertFaceFeature(faceID: string, faceFeature: string, callback: (result: FaceResult) => void): void;
|
|
37
|
+
addFaceBySDKImage(faceID: string, base64Image: string, callback: (result: FaceResult) => void): void;
|
|
38
|
+
deleteFaceFeature(faceID: string, callback: (result: FaceResult) => void): void;
|
|
39
|
+
}
|
package/lib/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@faceaisdk/react-native-face-sdk",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "React Native offline face enrollment, verification, and liveness detection SDK.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "FaceAISDK.Service@gmail.com",
|
|
7
|
+
"homepage": "https://github.com/FaceAISDK/FaceAISDK_RN",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/FaceAISDK/FaceAISDK_RN.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/FaceAISDK/FaceAISDK_RN/issues"
|
|
14
|
+
},
|
|
15
|
+
"source": "src/index.ts",
|
|
16
|
+
"main": "lib/index.js",
|
|
17
|
+
"types": "lib/index.d.ts",
|
|
18
|
+
"react-native": "src/index.ts",
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"files": [
|
|
21
|
+
"src",
|
|
22
|
+
"lib",
|
|
23
|
+
"android",
|
|
24
|
+
"ios",
|
|
25
|
+
"react-native-face-sdk.podspec",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.build.json",
|
|
31
|
+
"clean": "rm -rf lib",
|
|
32
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
33
|
+
"prepare": "npm run clean && npm run build",
|
|
34
|
+
"prepublishOnly": "npm run typecheck && npm run lint && npm test",
|
|
35
|
+
"test": "jest --runInBand",
|
|
36
|
+
"lint": "eslint src __tests__",
|
|
37
|
+
"pack": "npm pack .",
|
|
38
|
+
"package:build": "npm run build",
|
|
39
|
+
"package:typecheck": "npm run typecheck",
|
|
40
|
+
"package:pack": "npm run pack",
|
|
41
|
+
"start": "npm --prefix example run start",
|
|
42
|
+
"android": "npm --prefix example run android",
|
|
43
|
+
"ios": "npm --prefix example run ios",
|
|
44
|
+
"pods:install": "npm --prefix example run pods:install",
|
|
45
|
+
"example:test": "npm --prefix example run test"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"react-native",
|
|
49
|
+
"ios",
|
|
50
|
+
"android",
|
|
51
|
+
"face-recognition",
|
|
52
|
+
"liveness-detection",
|
|
53
|
+
"offline-face-sdk",
|
|
54
|
+
"biometrics",
|
|
55
|
+
"face-id"
|
|
56
|
+
],
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"react": ">=18.0.0",
|
|
59
|
+
"react-native": ">=0.71.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@babel/core": "^7.25.2",
|
|
63
|
+
"@babel/preset-env": "^7.25.3",
|
|
64
|
+
"@babel/runtime": "^7.25.0",
|
|
65
|
+
"@react-native-community/cli": "20.1.0",
|
|
66
|
+
"@react-native-community/cli-platform-android": "20.1.0",
|
|
67
|
+
"@react-native-community/cli-platform-ios": "20.1.0",
|
|
68
|
+
"@react-native/babel-preset": "0.84.0",
|
|
69
|
+
"@react-native/eslint-config": "0.84.0",
|
|
70
|
+
"@react-native/metro-config": "0.84.0",
|
|
71
|
+
"@react-native/typescript-config": "0.84.0",
|
|
72
|
+
"@types/jest": "^29.5.13",
|
|
73
|
+
"@types/react": "^19.2.0",
|
|
74
|
+
"@types/react-test-renderer": "^19.1.0",
|
|
75
|
+
"eslint": "^8.19.0",
|
|
76
|
+
"ios-deploy": "^1.12.2",
|
|
77
|
+
"jest": "^29.6.3",
|
|
78
|
+
"prettier": "2.8.8",
|
|
79
|
+
"react": "19.2.3",
|
|
80
|
+
"react-native": "0.84.0",
|
|
81
|
+
"react-native-safe-area-context": "^5.5.2",
|
|
82
|
+
"react-test-renderer": "19.2.3",
|
|
83
|
+
"typescript": "^5.8.3"
|
|
84
|
+
},
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">= 22.11.0"
|
|
87
|
+
}
|
|
88
|
+
}
|