@rick427/react-native-liveness 0.1.4 → 0.1.5

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.
@@ -8,8 +8,6 @@ import com.google.mlkit.vision.face.FaceDetectorOptions
8
8
  import com.mrousavy.camera.frameprocessors.Frame
9
9
  import com.mrousavy.camera.frameprocessors.FrameProcessorPlugin
10
10
 
11
- // Vision Camera v4.5+ removed VisionCameraProxyHolder and made FrameProcessorPlugin
12
- // a no-arg constructor. The proxy/options are no longer passed at construction time.
13
11
  class LivenessCameraPlugin : FrameProcessorPlugin() {
14
12
 
15
13
  private val faceDetector = FaceDetection.getClient(
@@ -23,8 +21,7 @@ class LivenessCameraPlugin : FrameProcessorPlugin() {
23
21
 
24
22
  /**
25
23
  * Convert Vision Camera's Orientation to ML Kit rotation degrees.
26
- * Uses toString() comparison to stay resilient across VC patch versions
27
- * where the Orientation API (toDegrees / toSurfaceRotation) may vary.
24
+ * Uses toString() to stay resilient across VC patch-version API churn.
28
25
  */
29
26
  private fun orientationDegrees(frame: Frame): Int {
30
27
  val name = frame.orientation.toString().uppercase()
@@ -52,20 +49,23 @@ class LivenessCameraPlugin : FrameProcessorPlugin() {
52
49
  val face = faces.first()
53
50
  val box = face.boundingBox
54
51
 
52
+ // IMPORTANT: All numeric values must be Double, not Float.
53
+ // JSI (Vision Camera's JS bridge) cannot convert Java Float → jsi::Value
54
+ // and throws "Cannot convert Java type class java.lang.Float" at runtime.
55
55
  mapOf(
56
56
  "detected" to true,
57
57
  "bounds" to mapOf(
58
- "x" to box.left.toFloat(),
59
- "y" to box.top.toFloat(),
60
- "width" to box.width().toFloat(),
61
- "height" to box.height().toFloat()
58
+ "x" to box.left.toDouble(),
59
+ "y" to box.top.toDouble(),
60
+ "width" to box.width().toDouble(),
61
+ "height" to box.height().toDouble()
62
62
  ),
63
- "yawAngle" to face.headEulerAngleY,
64
- "pitchAngle" to face.headEulerAngleX,
65
- "rollAngle" to face.headEulerAngleZ,
66
- "leftEyeOpenProbability" to (face.leftEyeOpenProbability ?: -1f),
67
- "rightEyeOpenProbability" to (face.rightEyeOpenProbability ?: -1f),
68
- "smilingProbability" to (face.smilingProbability ?: -1f)
63
+ "yawAngle" to face.headEulerAngleY.toDouble(),
64
+ "pitchAngle" to face.headEulerAngleX.toDouble(),
65
+ "rollAngle" to face.headEulerAngleZ.toDouble(),
66
+ "leftEyeOpenProbability" to (face.leftEyeOpenProbability?.toDouble() ?: -1.0),
67
+ "rightEyeOpenProbability" to (face.rightEyeOpenProbability?.toDouble() ?: -1.0),
68
+ "smilingProbability" to (face.smilingProbability?.toDouble() ?: -1.0)
69
69
  )
70
70
  } catch (e: Exception) {
71
71
  mapOf("detected" to false)
@@ -3,23 +3,36 @@
3
3
  import { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { Animated, StyleSheet, Text, View } from 'react-native';
5
5
  import { Camera, useCameraDevice, useCameraPermission } from 'react-native-vision-camera';
6
- import { Circle, Ellipse, Path, Svg } from 'react-native-svg';
6
+ import { Ellipse, Path, Svg } from 'react-native-svg';
7
7
  import { useLivenessCamera } from "./useLivenessCamera.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
- const OVAL_H_RATIO = 0.55;
10
- const OVAL_V_RATIO = 0.72;
9
+ // Oval is sized relative to container WIDTH only, so it stays face-shaped
10
+ // on any screen. ry = rx * FACE_RATIO gives a natural portrait face oval.
11
+ const OVAL_WIDTH_RATIO = 0.72; // oval width = 72 % of container width
12
+ const FACE_RATIO = 1.35; // height-to-width ratio of the oval (~3:4 face)
11
13
  const STROKE_WIDTH = 3;
12
14
  // Cubic bezier approximation constant for a smooth ellipse
13
15
  const K = 0.5523;
14
- function getOvalColor(state) {
16
+
17
+ /**
18
+ * Returns the stroke colour for the oval guide.
19
+ *
20
+ * ● White – no face / scanning (score < 0.4)
21
+ * ● Yellow – face detected, confidence building (0.4 ≤ score < threshold)
22
+ * ● Green – liveness confirmed / countdown / capture
23
+ * ● Red – error
24
+ */
25
+ function getOvalColor(state, score) {
15
26
  switch (state) {
27
+ case 'error':
28
+ return '#FF3B30';
16
29
  case 'confirmed':
17
30
  case 'countdown':
18
31
  case 'capturing':
19
32
  case 'done':
20
33
  return '#4CAF50';
21
34
  default:
22
- return '#FFFFFF';
35
+ return score >= 0.4 ? '#FFD60A' : '#FFFFFF';
23
36
  }
24
37
  }
25
38
 
@@ -34,18 +47,19 @@ function ellipsePath(cx, cy, rx, ry) {
34
47
  function OvalOverlay({
35
48
  width,
36
49
  height,
37
- state
50
+ state,
51
+ score
38
52
  }) {
39
53
  if (width === 0 || height === 0) return null;
40
54
  const cx = width / 2;
41
- const cy = height / 2;
42
- const rx = width * OVAL_H_RATIO / 2;
43
- const ry = height * OVAL_V_RATIO / 2;
44
- const color = getOvalColor(state);
55
+ // Shift centre slightly above midpoint so the face sits naturally in frame
56
+ const cy = height * 0.45;
57
+ const rx = width * OVAL_WIDTH_RATIO / 2;
58
+ const ry = rx * FACE_RATIO;
59
+ const color = getOvalColor(state, score);
45
60
 
46
61
  // Compound path: outer rect + oval. evenodd fill rule makes the oval transparent.
47
62
  const scrimD = `M0 0H${width}V${height}H0Z ${ellipsePath(cx, cy, rx, ry)}`;
48
- const showDot = state === 'confirmed' || state === 'countdown' || state === 'capturing';
49
63
  return /*#__PURE__*/_jsxs(Svg, {
50
64
  style: StyleSheet.absoluteFill,
51
65
  width: width,
@@ -62,11 +76,6 @@ function OvalOverlay({
62
76
  fill: "none",
63
77
  stroke: color,
64
78
  strokeWidth: STROKE_WIDTH
65
- }), showDot && /*#__PURE__*/_jsx(Circle, {
66
- cx: cx,
67
- cy: cy - ry - 8,
68
- r: 5,
69
- fill: color
70
79
  })]
71
80
  });
72
81
  }
@@ -138,6 +147,7 @@ export function LivenessCamera({
138
147
  const {
139
148
  frameProcessor,
140
149
  livenessState,
150
+ livenessScore,
141
151
  countdown,
142
152
  feedback
143
153
  } = useLivenessCamera({
@@ -199,7 +209,8 @@ export function LivenessCamera({
199
209
  }), /*#__PURE__*/_jsx(OvalOverlay, {
200
210
  width: containerSize.width,
201
211
  height: containerSize.height,
202
- state: livenessState
212
+ state: livenessState,
213
+ score: livenessScore
203
214
  }), livenessState !== 'done' && /*#__PURE__*/_jsx(View, {
204
215
  style: styles.feedbackContainer,
205
216
  children: /*#__PURE__*/_jsx(Text, {
@@ -235,7 +246,7 @@ const styles = StyleSheet.create({
235
246
  },
236
247
  feedbackContainer: {
237
248
  position: 'absolute',
238
- bottom: '14%',
249
+ bottom: '12%',
239
250
  left: 0,
240
251
  right: 0,
241
252
  alignItems: 'center',
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraPermission","Circle","Ellipse","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","OVAL_H_RATIO","OVAL_V_RATIO","STROKE_WIDTH","K","getOvalColor","state","ellipsePath","cx","cy","rx","ry","join","OvalOverlay","width","height","color","scrimD","showDot","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","r","CountdownBubble","value","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","fontWeight","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraPermission","Ellipse","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","OVAL_WIDTH_RATIO","FACE_RATIO","STROKE_WIDTH","K","getOvalColor","state","score","ellipsePath","cx","cy","rx","ry","join","OvalOverlay","width","height","color","scrimD","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","CountdownBubble","value","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","livenessScore","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","fontWeight","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AA2HlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,KAAK,GACN,EAAE,mBAAmB,2CAiFrB"}
1
+ {"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAsIlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,KAAK,GACN,EAAE,mBAAmB,2CAkFrB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rick427/react-native-liveness",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Liveness detection library for React Native using Vision Camera v4 and ML Kit",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -5,25 +5,37 @@ import {
5
5
  useCameraDevice,
6
6
  useCameraPermission,
7
7
  } from 'react-native-vision-camera';
8
- import { Circle, Ellipse, Path, Svg } from 'react-native-svg';
8
+ import { Ellipse, Path, Svg } from 'react-native-svg';
9
9
  import { useLivenessCamera } from './useLivenessCamera';
10
10
  import type { LivenessCameraProps, LivenessState } from './types';
11
11
 
12
- const OVAL_H_RATIO = 0.55;
13
- const OVAL_V_RATIO = 0.72;
12
+ // Oval is sized relative to container WIDTH only, so it stays face-shaped
13
+ // on any screen. ry = rx * FACE_RATIO gives a natural portrait face oval.
14
+ const OVAL_WIDTH_RATIO = 0.72; // oval width = 72 % of container width
15
+ const FACE_RATIO = 1.35; // height-to-width ratio of the oval (~3:4 face)
14
16
  const STROKE_WIDTH = 3;
15
17
  // Cubic bezier approximation constant for a smooth ellipse
16
18
  const K = 0.5523;
17
19
 
18
- function getOvalColor(state: LivenessState): string {
20
+ /**
21
+ * Returns the stroke colour for the oval guide.
22
+ *
23
+ * ● White – no face / scanning (score < 0.4)
24
+ * ● Yellow – face detected, confidence building (0.4 ≤ score < threshold)
25
+ * ● Green – liveness confirmed / countdown / capture
26
+ * ● Red – error
27
+ */
28
+ function getOvalColor(state: LivenessState, score: number): string {
19
29
  switch (state) {
30
+ case 'error':
31
+ return '#FF3B30';
20
32
  case 'confirmed':
21
33
  case 'countdown':
22
34
  case 'capturing':
23
35
  case 'done':
24
36
  return '#4CAF50';
25
37
  default:
26
- return '#FFFFFF';
38
+ return score >= 0.4 ? '#FFD60A' : '#FFFFFF';
27
39
  }
28
40
  }
29
41
 
@@ -47,25 +59,25 @@ function OvalOverlay({
47
59
  width,
48
60
  height,
49
61
  state,
62
+ score,
50
63
  }: {
51
64
  width: number;
52
65
  height: number;
53
66
  state: LivenessState;
67
+ score: number;
54
68
  }) {
55
69
  if (width === 0 || height === 0) return null;
56
70
 
57
71
  const cx = width / 2;
58
- const cy = height / 2;
59
- const rx = (width * OVAL_H_RATIO) / 2;
60
- const ry = (height * OVAL_V_RATIO) / 2;
61
- const color = getOvalColor(state);
72
+ // Shift centre slightly above midpoint so the face sits naturally in frame
73
+ const cy = height * 0.45;
74
+ const rx = (width * OVAL_WIDTH_RATIO) / 2;
75
+ const ry = rx * FACE_RATIO;
76
+ const color = getOvalColor(state, score);
62
77
 
63
78
  // Compound path: outer rect + oval. evenodd fill rule makes the oval transparent.
64
79
  const scrimD = `M0 0H${width}V${height}H0Z ${ellipsePath(cx, cy, rx, ry)}`;
65
80
 
66
- const showDot =
67
- state === 'confirmed' || state === 'countdown' || state === 'capturing';
68
-
69
81
  return (
70
82
  <Svg style={StyleSheet.absoluteFill} width={width} height={height}>
71
83
  <Path d={scrimD} fill="rgba(0,0,0,0.55)" fillRule="evenodd" />
@@ -78,7 +90,6 @@ function OvalOverlay({
78
90
  stroke={color}
79
91
  strokeWidth={STROKE_WIDTH}
80
92
  />
81
- {showDot && <Circle cx={cx} cy={cy - ry - 8} r={5} fill={color} />}
82
93
  </Svg>
83
94
  );
84
95
  }
@@ -145,7 +156,7 @@ export function LivenessCamera({
145
156
  const cameraRef = useRef<Camera>(null);
146
157
  const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
147
158
 
148
- const { frameProcessor, livenessState, countdown, feedback } =
159
+ const { frameProcessor, livenessState, livenessScore, countdown, feedback } =
149
160
  useLivenessCamera({
150
161
  livenessThreshold,
151
162
  confirmFrames,
@@ -204,6 +215,7 @@ export function LivenessCamera({
204
215
  width={containerSize.width}
205
216
  height={containerSize.height}
206
217
  state={livenessState}
218
+ score={livenessScore}
207
219
  />
208
220
  {livenessState !== 'done' && (
209
221
  <View style={styles.feedbackContainer}>
@@ -240,7 +252,7 @@ const styles = StyleSheet.create({
240
252
  },
241
253
  feedbackContainer: {
242
254
  position: 'absolute',
243
- bottom: '14%',
255
+ bottom: '12%',
244
256
  left: 0,
245
257
  right: 0,
246
258
  alignItems: 'center',