From a15e4c7076dd85ffeb53fed40ea8607d3cd9a0a9 Mon Sep 17 00:00:00 2001 From: Johannes Klein Date: Wed, 13 Aug 2025 19:20:36 +0200 Subject: [PATCH] Allow some time for camera initialization, add log if no device found (#3059) * Start screen in a loading state for 700ms One idea I have about the bug is that maybe it takes a few renders to initialize the device. As we are immediately backing out of the screen in case we have no device, maybe just adding a few ms to give the native side time helps. * If no device is selected log the length of devices available * Add missing mock --- __mocks__/react-native-vision-camera.ts | 2 + src/components/Camera/CameraContainer.tsx | 35 ++++++++++++++++- .../helpers/visionCameraWrapper.e2e-mock | 3 ++ .../Camera/helpers/visionCameraWrapper.js | 7 +++- tests/vision-camera/vision-camera.js | 38 +++++++++---------- 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/__mocks__/react-native-vision-camera.ts b/__mocks__/react-native-vision-camera.ts index f822498a2..f46d77e3d 100644 --- a/__mocks__/react-native-vision-camera.ts +++ b/__mocks__/react-native-vision-camera.ts @@ -2,12 +2,14 @@ import { mockCamera, mockSortDevices, mockUseCameraDevice, + mockUseCameraDevices, mockUseCameraFormat } from "tests/vision-camera/vision-camera"; export const Camera = mockCamera; export const sortDevices = mockSortDevices; export const useCameraDevice = mockUseCameraDevice; +export const useCameraDevices = mockUseCameraDevices; export const useCameraFormat = mockUseCameraFormat; export const VisionCameraProxy = { initFrameProcessorPlugin: jest.fn( ) diff --git a/src/components/Camera/CameraContainer.tsx b/src/components/Camera/CameraContainer.tsx index 291c67853..f230bd9a5 100644 --- a/src/components/Camera/CameraContainer.tsx +++ b/src/components/Camera/CameraContainer.tsx @@ -1,7 +1,11 @@ import { useNavigation, useRoute } from "@react-navigation/native"; import { - Camera, useCameraDevice + Camera, + useCameraDevice, + useCameraDevices } from "components/Camera/helpers/visionCameraWrapper"; +import { ActivityIndicator } from "components/SharedComponents"; +import { View } from "components/styledComponents"; import React, { useCallback, useEffect, @@ -14,6 +18,7 @@ import type { TakePhotoOptions } from "react-native-vision-camera"; import fetchAccurateUserLocation from "sharedHelpers/fetchAccurateUserLocation.ts"; +import { log } from "sharedHelpers/logger"; import { createSentinelFile, deleteSentinelFile, logStage } from "sharedHelpers/sentinelFiles.ts"; import { useTranslation } from "sharedHooks"; import useLocationPermission from "sharedHooks/useLocationPermission.tsx"; @@ -50,6 +55,8 @@ interface SavePhotoOptions { export const MAX_PHOTOS_ALLOWED = 20; +const logger = log.extend( "CameraContainer" ); + const CameraContainer = ( ) => { const currentObservation = useStore( state => state.currentObservation ); const setCameraState = useStore( state => state.setCameraState ); @@ -99,6 +106,18 @@ const CameraContainer = ( ) => { "telephoto-camera" ] } ); + const devices = useCameraDevices( ); + const [loadingDevices, setLoadingDevices] = useState( true ); + const [timeoutId, setTimeoutId] = useState | null>( + undefined + ); + if ( timeoutId === undefined ) { + setTimeoutId( setTimeout( () => { + setLoadingDevices( false ); + setTimeoutId( null ); + }, 700 ) ); + } + const hasFlash = device?.hasFlash; const initialPhotoOptions = { // We had this set to true in Seek but received many reports of it not respecting OS-wide sound @@ -278,11 +297,23 @@ const CameraContainer = ( ) => { } }, [hasLocationPermissions] ); - if ( !device ) { + if ( loadingDevices ) { + return ( + + + + ); + } + + if ( !loadingDevices && !device ) { Alert.alert( t( "No-Camera-Available" ), t( "Could-not-find-a-camera-on-this-device" ) ); + logger.error( + "Camera started but no device was found. Length of the list of all devices: ", + devices.length + ); navigation.goBack(); return null; } diff --git a/src/components/Camera/helpers/visionCameraWrapper.e2e-mock b/src/components/Camera/helpers/visionCameraWrapper.e2e-mock index 97861a8d9..fef42c8c6 100644 --- a/src/components/Camera/helpers/visionCameraWrapper.e2e-mock +++ b/src/components/Camera/helpers/visionCameraWrapper.e2e-mock @@ -10,16 +10,19 @@ import { useFrameProcessor } from "react-native-vision-camera"; import { mockCamera, mockUseCameraDevice, + mockUseCameraDevices, mockUseCameraFormat } from "tests/vision-camera/vision-camera"; const Camera = mockCamera; const useCameraDevice = mockUseCameraDevice; +const useCameraDevices = mockUseCameraDevices; const useCameraFormat = mockUseCameraFormat; export { Camera, useCameraDevice, + useCameraDevices, useCameraFormat, useFrameProcessor }; diff --git a/src/components/Camera/helpers/visionCameraWrapper.js b/src/components/Camera/helpers/visionCameraWrapper.js index f2a59d323..9a12787f7 100644 --- a/src/components/Camera/helpers/visionCameraWrapper.js +++ b/src/components/Camera/helpers/visionCameraWrapper.js @@ -3,10 +3,15 @@ import { Camera, useCameraDevice, + useCameraDevices, useCameraFormat, useFrameProcessor } from "react-native-vision-camera"; export { - Camera, useCameraDevice, useCameraFormat, useFrameProcessor + Camera, + useCameraDevice, + useCameraDevices, + useCameraFormat, + useFrameProcessor }; diff --git a/tests/vision-camera/vision-camera.js b/tests/vision-camera/vision-camera.js index 23dbedc2c..548d72979 100644 --- a/tests/vision-camera/vision-camera.js +++ b/tests/vision-camera/vision-camera.js @@ -104,27 +104,27 @@ export class mockCamera extends React.PureComponent { export const mockSortDevices = ( _left, _right ) => 1; -export const mockUseCameraDevice = _deviceType => { - const device = { - physicalDevices: ["wide-angle-camera"], - hasFlash: true, - hasTorch: true, - id: "1", - isMultiCam: true, - maxZoom: 12.931958198547363, - minZoom: 1, - name: "front (1)", - neutralZoom: 1, - position: "front", - supportsDepthCapture: false, - supportsFocus: true, - supportsLowLightBoost: false, - supportsParallelVideoProcessing: true, - supportsRawCapture: true - }; - return device; +const device = { + physicalDevices: ["wide-angle-camera"], + hasFlash: true, + hasTorch: true, + id: "1", + isMultiCam: true, + maxZoom: 12.931958198547363, + minZoom: 1, + name: "front (1)", + neutralZoom: 1, + position: "front", + supportsDepthCapture: false, + supportsFocus: true, + supportsLowLightBoost: false, + supportsParallelVideoProcessing: true, + supportsRawCapture: true }; +export const mockUseCameraDevice = _deviceType => device; +export const mockUseCameraDevices = () => [device]; + export const mockUseCameraFormat = _device => { const format = { autoFocusSystem: "contrast-detection",