1import { UnavailabilityError } from 'expo-modules-core'; 2import { CameraType, PermissionStatus, } from './Camera.types'; 3import { canGetUserMedia, isBackCameraAvailableAsync, isFrontCameraAvailableAsync, } from './WebUserMediaManager'; 4function getUserMedia(constraints) { 5 if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { 6 return navigator.mediaDevices.getUserMedia(constraints); 7 } 8 // Some browsers partially implement mediaDevices. We can't just assign an object 9 // with getUserMedia as it would overwrite existing properties. 10 // Here, we will just add the getUserMedia property if it's missing. 11 // First get ahold of the legacy getUserMedia, if present 12 const getUserMedia = 13 // TODO: this method is deprecated, migrate to https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia 14 navigator.getUserMedia || 15 navigator.webkitGetUserMedia || 16 navigator.mozGetUserMedia || 17 function () { 18 const error = new Error('Permission unimplemented'); 19 error.code = 0; 20 error.name = 'NotAllowedError'; 21 throw error; 22 }; 23 return new Promise((resolve, reject) => { 24 getUserMedia.call(navigator, constraints, resolve, reject); 25 }); 26} 27function handleGetUserMediaError({ message }) { 28 // name: NotAllowedError 29 // code: 0 30 if (message === 'Permission dismissed') { 31 return { 32 status: PermissionStatus.UNDETERMINED, 33 expires: 'never', 34 canAskAgain: true, 35 granted: false, 36 }; 37 } 38 else { 39 // TODO: Bacon: [OSX] The system could deny access to chrome. 40 // TODO: Bacon: add: { status: 'unimplemented' } 41 return { 42 status: PermissionStatus.DENIED, 43 expires: 'never', 44 canAskAgain: true, 45 granted: false, 46 }; 47 } 48} 49async function handleRequestPermissionsAsync() { 50 try { 51 await getUserMedia({ 52 video: true, 53 }); 54 return { 55 status: PermissionStatus.GRANTED, 56 expires: 'never', 57 canAskAgain: true, 58 granted: true, 59 }; 60 } 61 catch ({ message }) { 62 return handleGetUserMediaError({ message }); 63 } 64} 65async function handlePermissionsQueryAsync(query) { 66 if (!navigator?.permissions?.query) { 67 throw new UnavailabilityError('expo-camera', 'navigator.permissions API is not available'); 68 } 69 try { 70 const { state } = await navigator.permissions.query({ name: query }); 71 switch (state) { 72 case 'prompt': 73 return { 74 status: PermissionStatus.UNDETERMINED, 75 expires: 'never', 76 canAskAgain: true, 77 granted: false, 78 }; 79 case 'granted': 80 return { 81 status: PermissionStatus.GRANTED, 82 expires: 'never', 83 canAskAgain: true, 84 granted: true, 85 }; 86 case 'denied': 87 return { 88 status: PermissionStatus.DENIED, 89 expires: 'never', 90 canAskAgain: true, 91 granted: false, 92 }; 93 } 94 } 95 catch (e) { 96 // Firefox doesn't support querying for the camera permission, so return undetermined status 97 if (e instanceof TypeError) { 98 return { 99 status: PermissionStatus.UNDETERMINED, 100 expires: 'never', 101 canAskAgain: true, 102 granted: false, 103 }; 104 } 105 throw e; 106 } 107} 108export default { 109 get name() { 110 return 'ExponentCameraManager'; 111 }, 112 get Type() { 113 return { 114 back: 'back', 115 front: 'front', 116 }; 117 }, 118 get FlashMode() { 119 return { 120 on: 'on', 121 off: 'off', 122 auto: 'auto', 123 torch: 'torch', 124 }; 125 }, 126 get AutoFocus() { 127 return { 128 on: 'on', 129 off: 'off', 130 auto: 'auto', 131 singleShot: 'singleShot', 132 }; 133 }, 134 get WhiteBalance() { 135 return { 136 auto: 'auto', 137 continuous: 'continuous', 138 manual: 'manual', 139 }; 140 }, 141 get VideoQuality() { 142 return {}; 143 }, 144 get VideoStabilization() { 145 return {}; 146 }, 147 async isAvailableAsync() { 148 return canGetUserMedia(); 149 }, 150 async takePicture(options, camera) { 151 return await camera.takePicture(options); 152 }, 153 async pausePreview(camera) { 154 await camera.pausePreview(); 155 }, 156 async resumePreview(camera) { 157 return await camera.resumePreview(); 158 }, 159 async getAvailableCameraTypesAsync() { 160 if (!canGetUserMedia() || !navigator.mediaDevices.enumerateDevices) 161 return []; 162 const devices = await navigator.mediaDevices.enumerateDevices(); 163 const types = await Promise.all([ 164 (await isFrontCameraAvailableAsync(devices)) && CameraType.front, 165 (await isBackCameraAvailableAsync()) && CameraType.back, 166 ]); 167 return types.filter(Boolean); 168 }, 169 async getAvailablePictureSizes(ratio, camera) { 170 return await camera.getAvailablePictureSizes(ratio); 171 }, 172 /* async getSupportedRatios(camera: ExponentCameraRef): Promise<string[]> { 173 // TODO: Support on web 174 }, 175 async record( 176 options?: CameraRecordingOptions, 177 camera: ExponentCameraRef 178 ): Promise<{ uri: string }> { 179 // TODO: Support on web 180 }, 181 async stopRecording(camera: ExponentCameraRef): Promise<void> { 182 // TODO: Support on web 183 }, */ 184 async getPermissionsAsync() { 185 return handlePermissionsQueryAsync('camera'); 186 }, 187 async requestPermissionsAsync() { 188 return handleRequestPermissionsAsync(); 189 }, 190 async getCameraPermissionsAsync() { 191 return handlePermissionsQueryAsync('camera'); 192 }, 193 async requestCameraPermissionsAsync() { 194 return handleRequestPermissionsAsync(); 195 }, 196 async getMicrophonePermissionsAsync() { 197 return handlePermissionsQueryAsync('microphone'); 198 }, 199 async requestMicrophonePermissionsAsync() { 200 try { 201 await getUserMedia({ 202 audio: true, 203 }); 204 return { 205 status: PermissionStatus.GRANTED, 206 expires: 'never', 207 canAskAgain: true, 208 granted: true, 209 }; 210 } 211 catch ({ message }) { 212 return handleGetUserMediaError({ message }); 213 } 214 }, 215}; 216//# sourceMappingURL=ExponentCameraManager.web.js.map