@hexar/biometric-identity-sdk-react-native 1.22.0 → 1.23.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,CAwhCtD,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,CAqiCtD,CAAC;AA4PF,eAAe,aAAa,CAAC"}
@@ -102,12 +102,20 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
102
102
  const [guidanceText, setGuidanceText] = (0, react_1.useState)(null);
103
103
  const [hasPermission, setHasPermission] = (0, react_1.useState)(false);
104
104
  const [isCameraReady, setIsCameraReady] = (0, react_1.useState)(false);
105
+ // Controlled via stopRecording — set to false before calling onComplete so
106
+ // VisionCamera tears down the native session and any in-flight takePhoto()
107
+ // calls fail gracefully instead of crashing with IllegalViewOperationException
108
+ // when the parent unmounts the <Camera> view underneath them.
109
+ const [isCameraActive, setIsCameraActive] = (0, react_1.useState)(true);
105
110
  const cameraRef = (0, react_1.useRef)(null);
106
111
  const { hasPermission: cameraPermission, requestPermission } = (0, react_native_vision_camera_1.useCameraPermission)();
107
112
  // Prefer wide-angle (main) front camera — some Android devices expose
108
113
  // multiple front sensors and the default may lack autofocus.
114
+ // Only request wide-angle — ultra-wide opens a multi-physical-camera session
115
+ // that is slow to release, causing CameraViewModule.findCameraView to throw
116
+ // IllegalViewOperationException when the component unmounts after recording.
109
117
  const device = (0, react_native_vision_camera_1.useCameraDevice)('front', {
110
- physicalDevices: ['wide-angle-camera', 'ultra-wide-angle-camera'],
118
+ physicalDevices: ['wide-angle-camera'],
111
119
  });
112
120
  const [deviceReady, setDeviceReady] = (0, react_1.useState)(!!device);
113
121
  const fadeAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
@@ -307,6 +315,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
307
315
  setChallengeProgress(0);
308
316
  setOverallProgress(0);
309
317
  recordingStartTime.current = 0;
318
+ setIsCameraActive(true);
310
319
  setPhase('countdown');
311
320
  setCountdown(3);
312
321
  }, []);
@@ -380,11 +389,11 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
380
389
  };
381
390
  biometric_identity_sdk_core_1.logger.info('Video recording completed:', result.frames.length, 'frames,', (actualDuration / 1000).toFixed(1) + 's', 'challenges:', currentCompletedChallenges.length);
382
391
  isRecordingRef.current = false;
383
- // Delay onComplete so Vision Camera's native session (especially
384
- // multi-physical-camera sessions opened via physicalDevices) has time
385
- // to fully close before the parent unmounts the <Camera> view.
386
- // Without this delay, CameraViewModule.findCameraView throws
387
- // IllegalViewOperationException on Android 13 (SDK 33).
392
+ // Deactivate the camera first so VisionCamera tears down its native
393
+ // session. Any in-flight takePhoto() calls will then fail gracefully
394
+ // rather than crashing with IllegalViewOperationException when the
395
+ // parent unmounts the <Camera> view underneath them.
396
+ setIsCameraActive(false);
388
397
  setTimeout(() => onComplete(result), 250);
389
398
  }
390
399
  catch (error) {
@@ -536,6 +545,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
536
545
  sessionId,
537
546
  };
538
547
  setPhase('processing');
548
+ setIsCameraActive(false);
539
549
  setTimeout(() => onComplete(result), 250);
540
550
  }
541
551
  else {
@@ -549,6 +559,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
549
559
  sessionId,
550
560
  };
551
561
  setPhase('processing');
562
+ setIsCameraActive(false);
552
563
  setTimeout(() => onComplete(result), 250);
553
564
  }
554
565
  }
@@ -580,6 +591,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
580
591
  challengesCompleted: currentCompletedChallenges,
581
592
  sessionId,
582
593
  };
594
+ setIsCameraActive(false);
583
595
  setTimeout(() => {
584
596
  onComplete(result);
585
597
  }, 250);
@@ -626,6 +638,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
626
638
  sessionId,
627
639
  };
628
640
  setPhase('processing');
641
+ setIsCameraActive(false);
629
642
  setTimeout(() => onComplete(result), 250);
630
643
  }
631
644
  }
@@ -807,7 +820,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
807
820
  }
808
821
  return (react_1.default.createElement(react_native_1.View, { style: styles.container },
809
822
  react_1.default.createElement(react_native_1.View, { style: styles.cameraContainer },
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) => {
823
+ react_1.default.createElement(react_native_vision_camera_1.Camera, { ref: cameraRef, style: react_native_1.StyleSheet.absoluteFill, device: device, isActive: isCameraActive, video: true, photo: true, audio: false, onInitialized: () => setIsCameraReady(true), onError: (error) => {
811
824
  biometric_identity_sdk_core_1.logger.error('Camera hardware error:', error);
812
825
  handleRecordingError(error);
813
826
  } }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-react-native",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "description": "React Native wrapper for Biometric Identity SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -131,12 +131,20 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
131
131
  const [guidanceText, setGuidanceText] = useState<string | null>(null);
132
132
  const [hasPermission, setHasPermission] = useState(false);
133
133
  const [isCameraReady, setIsCameraReady] = useState(false);
134
+ // Controlled via stopRecording — set to false before calling onComplete so
135
+ // VisionCamera tears down the native session and any in-flight takePhoto()
136
+ // calls fail gracefully instead of crashing with IllegalViewOperationException
137
+ // when the parent unmounts the <Camera> view underneath them.
138
+ const [isCameraActive, setIsCameraActive] = useState(true);
134
139
  const cameraRef = useRef<Camera>(null);
135
140
  const { hasPermission: cameraPermission, requestPermission } = useCameraPermission();
136
141
  // Prefer wide-angle (main) front camera — some Android devices expose
137
142
  // multiple front sensors and the default may lack autofocus.
143
+ // Only request wide-angle — ultra-wide opens a multi-physical-camera session
144
+ // that is slow to release, causing CameraViewModule.findCameraView to throw
145
+ // IllegalViewOperationException when the component unmounts after recording.
138
146
  const device = useCameraDevice('front', {
139
- physicalDevices: ['wide-angle-camera', 'ultra-wide-angle-camera'],
147
+ physicalDevices: ['wide-angle-camera'],
140
148
  });
141
149
  const [deviceReady, setDeviceReady] = useState(!!device);
142
150
 
@@ -356,6 +364,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
356
364
  setChallengeProgress(0);
357
365
  setOverallProgress(0);
358
366
  recordingStartTime.current = 0;
367
+ setIsCameraActive(true);
359
368
  setPhase('countdown');
360
369
  setCountdown(3);
361
370
  }, []);
@@ -450,11 +459,11 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
450
459
  logger.info('Video recording completed:', result.frames.length, 'frames,', (actualDuration / 1000).toFixed(1) + 's', 'challenges:', currentCompletedChallenges.length);
451
460
 
452
461
  isRecordingRef.current = false;
453
- // Delay onComplete so Vision Camera's native session (especially
454
- // multi-physical-camera sessions opened via physicalDevices) has time
455
- // to fully close before the parent unmounts the <Camera> view.
456
- // Without this delay, CameraViewModule.findCameraView throws
457
- // IllegalViewOperationException on Android 13 (SDK 33).
462
+ // Deactivate the camera first so VisionCamera tears down its native
463
+ // session. Any in-flight takePhoto() calls will then fail gracefully
464
+ // rather than crashing with IllegalViewOperationException when the
465
+ // parent unmounts the <Camera> view underneath them.
466
+ setIsCameraActive(false);
458
467
  setTimeout(() => onComplete(result), 250);
459
468
  } catch (error) {
460
469
  logger.error('Error processing video:', error);
@@ -622,6 +631,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
622
631
  sessionId,
623
632
  };
624
633
  setPhase('processing');
634
+ setIsCameraActive(false);
625
635
  setTimeout(() => onComplete(result), 250);
626
636
  } else {
627
637
  logger.info('Stopping recording with frames (fallback):', currentFrames.length);
@@ -634,6 +644,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
634
644
  sessionId,
635
645
  };
636
646
  setPhase('processing');
647
+ setIsCameraActive(false);
637
648
  setTimeout(() => onComplete(result), 250);
638
649
  }
639
650
  }
@@ -672,6 +683,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
672
683
  sessionId,
673
684
  };
674
685
 
686
+ setIsCameraActive(false);
675
687
  setTimeout(() => {
676
688
  onComplete(result);
677
689
  }, 250);
@@ -723,6 +735,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
723
735
  sessionId,
724
736
  };
725
737
  setPhase('processing');
738
+ setIsCameraActive(false);
726
739
  setTimeout(() => onComplete(result), 250);
727
740
  }
728
741
  }
@@ -978,7 +991,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
978
991
  ref={cameraRef}
979
992
  style={StyleSheet.absoluteFill}
980
993
  device={device}
981
- isActive={true}
994
+ isActive={isCameraActive}
982
995
  video={true}
983
996
  photo={true}
984
997
  audio={false}