@hexar/biometric-identity-sdk-react-native 1.21.0 → 1.22.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAexE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2CD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAigCtD,CAAC;AA4PF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAexE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2CD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAwhCtD,CAAC;AA4PF,eAAe,aAAa,CAAC"}
@@ -101,6 +101,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
101
101
  const framesRef = (0, react_1.useRef)([]);
102
102
  const [guidanceText, setGuidanceText] = (0, react_1.useState)(null);
103
103
  const [hasPermission, setHasPermission] = (0, react_1.useState)(false);
104
+ const [isCameraReady, setIsCameraReady] = (0, react_1.useState)(false);
104
105
  const cameraRef = (0, react_1.useRef)(null);
105
106
  const { hasPermission: cameraPermission, requestPermission } = (0, react_native_vision_camera_1.useCameraPermission)();
106
107
  // Prefer wide-angle (main) front camera — some Android devices expose
@@ -233,7 +234,11 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
233
234
  initChallenges();
234
235
  }, [propChallenges, instructions, onFetchChallenges, smartMode, duration, language]);
235
236
  (0, react_1.useEffect)(() => {
236
- if (phase !== 'countdown')
237
+ // Don't start countdown until the native camera session is ready.
238
+ // On Motorola and other slow-to-initialize Android OEMs, isActive becoming
239
+ // true before the native view is registered causes IllegalViewOperationException
240
+ // inside CameraViewModule.findCameraView. We wait for onInitialized to fire.
241
+ if (phase !== 'countdown' || !isCameraReady)
237
242
  return;
238
243
  // Trigger focus at the start of countdown so the camera has ~3s to lock focus
239
244
  if (countdown === 3)
@@ -257,7 +262,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
257
262
  else {
258
263
  startRecording();
259
264
  }
260
- }, [countdown, phase]);
265
+ }, [countdown, phase, isCameraReady]);
261
266
  (0, react_1.useEffect)(() => {
262
267
  if (phase === 'recording') {
263
268
  react_native_1.Animated.loop(react_native_1.Animated.sequence([
@@ -309,6 +314,19 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
309
314
  setPhase('loading');
310
315
  react_native_1.Alert.alert('Recording Error', 'Failed to record video. Please try again.', [{ text: 'OK', onPress: onCancel }]);
311
316
  }, [onCancel]);
317
+ // Safety timeout: if the camera session never fires onInitialized within
318
+ // 10 seconds, treat it as a hardware failure and send the user back.
319
+ (0, react_1.useEffect)(() => {
320
+ if (isCameraReady)
321
+ return;
322
+ const timeout = setTimeout(() => {
323
+ if (!isCameraReady) {
324
+ biometric_identity_sdk_core_1.logger.error('Camera onInitialized timed out — hardware may be unavailable');
325
+ handleRecordingError(new Error('Camera initialization timed out'));
326
+ }
327
+ }, 10000);
328
+ return () => clearTimeout(timeout);
329
+ }, [isCameraReady, handleRecordingError]);
312
330
  const handleVideoComplete = (0, react_1.useCallback)(async (video) => {
313
331
  if (phase === 'loading' && !video) {
314
332
  return;
@@ -789,7 +807,10 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
789
807
  }
790
808
  return (react_1.default.createElement(react_native_1.View, { style: styles.container },
791
809
  react_1.default.createElement(react_native_1.View, { style: styles.cameraContainer },
792
- react_1.default.createElement(react_native_vision_camera_1.Camera, { ref: cameraRef, style: react_native_1.StyleSheet.absoluteFill, device: device, isActive: phase === 'recording' || phase === 'countdown', video: true, photo: true, audio: false }),
810
+ react_1.default.createElement(react_native_vision_camera_1.Camera, { ref: cameraRef, style: react_native_1.StyleSheet.absoluteFill, device: device, isActive: true, video: true, photo: true, audio: false, onInitialized: () => setIsCameraReady(true), onError: (error) => {
811
+ biometric_identity_sdk_core_1.logger.error('Camera hardware error:', error);
812
+ handleRecordingError(error);
813
+ } }),
793
814
  react_1.default.createElement(react_native_1.View, { style: styles.overlay },
794
815
  react_1.default.createElement(react_native_1.View, { style: [
795
816
  styles.faceOval,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-react-native",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "React Native wrapper for Biometric Identity SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -130,6 +130,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
130
130
  const framesRef = useRef<string[]>([]);
131
131
  const [guidanceText, setGuidanceText] = useState<string | null>(null);
132
132
  const [hasPermission, setHasPermission] = useState(false);
133
+ const [isCameraReady, setIsCameraReady] = useState(false);
133
134
  const cameraRef = useRef<Camera>(null);
134
135
  const { hasPermission: cameraPermission, requestPermission } = useCameraPermission();
135
136
  // Prefer wide-angle (main) front camera — some Android devices expose
@@ -274,7 +275,11 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
274
275
  }, [propChallenges, instructions, onFetchChallenges, smartMode, duration, language]);
275
276
 
276
277
  useEffect(() => {
277
- if (phase !== 'countdown') return;
278
+ // Don't start countdown until the native camera session is ready.
279
+ // On Motorola and other slow-to-initialize Android OEMs, isActive becoming
280
+ // true before the native view is registered causes IllegalViewOperationException
281
+ // inside CameraViewModule.findCameraView. We wait for onInitialized to fire.
282
+ if (phase !== 'countdown' || !isCameraReady) return;
278
283
 
279
284
  // Trigger focus at the start of countdown so the camera has ~3s to lock focus
280
285
  if (countdown === 3) triggerFocus();
@@ -298,7 +303,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
298
303
  } else {
299
304
  startRecording();
300
305
  }
301
- }, [countdown, phase]);
306
+ }, [countdown, phase, isCameraReady]);
302
307
 
303
308
  useEffect(() => {
304
309
  if (phase === 'recording') {
@@ -364,6 +369,19 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
364
369
  );
365
370
  }, [onCancel]);
366
371
 
372
+ // Safety timeout: if the camera session never fires onInitialized within
373
+ // 10 seconds, treat it as a hardware failure and send the user back.
374
+ useEffect(() => {
375
+ if (isCameraReady) return;
376
+ const timeout = setTimeout(() => {
377
+ if (!isCameraReady) {
378
+ logger.error('Camera onInitialized timed out — hardware may be unavailable');
379
+ handleRecordingError(new Error('Camera initialization timed out'));
380
+ }
381
+ }, 10000);
382
+ return () => clearTimeout(timeout);
383
+ }, [isCameraReady, handleRecordingError]);
384
+
367
385
  const handleVideoComplete = useCallback(async (video: any) => {
368
386
  if (phase === 'loading' && !video) {
369
387
  return;
@@ -960,10 +978,15 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
960
978
  ref={cameraRef}
961
979
  style={StyleSheet.absoluteFill}
962
980
  device={device}
963
- isActive={phase === 'recording' || phase === 'countdown'}
981
+ isActive={true}
964
982
  video={true}
965
983
  photo={true}
966
984
  audio={false}
985
+ onInitialized={() => setIsCameraReady(true)}
986
+ onError={(error) => {
987
+ logger.error('Camera hardware error:', error);
988
+ handleRecordingError(error);
989
+ }}
967
990
  />
968
991
 
969
992
  {/* Face Oval Overlay */}