UNPKG

8.12 kBJavaScriptView Raw
1// @flow
2import React from 'react';
3import PropTypes from 'prop-types';
4import mapValues from 'lodash.mapvalues';
5import { NativeModulesProxy, requireNativeViewManager } from 'expo-core';
6import { findNodeHandle, ViewPropTypes, Platform } from 'react-native';
7
8type PictureOptions = {
9 quality?: number,
10 base64?: boolean,
11 exif?: boolean,
12 skipProcessing?: boolean,
13 onPictureSaved?: Function,
14 // internal
15 id?: number,
16 fastMode?: boolean,
17};
18
19type RecordingOptions = {
20 maxDuration?: number,
21 maxFileSize?: number,
22 quality?: number | string,
23};
24
25type CapturedPicture = {
26 width: number,
27 height: number,
28 uri: string,
29 base64?: string,
30 exif?: Object,
31};
32
33type RecordingResult = {
34 uri: string,
35};
36
37type EventCallbackArgumentsType = {
38 nativeEvent: Object,
39};
40
41type MountErrorNativeEventType = {
42 message: string,
43};
44
45type PictureSavedNativeEventType = {
46 data: CapturedPicture,
47 id: number,
48};
49
50type PropsType = ViewPropTypes & {
51 zoom?: number,
52 ratio?: string,
53 focusDepth?: number,
54 type?: number | string,
55 onCameraReady?: Function,
56 useCamera2Api?: boolean,
57 flashMode?: number | string,
58 whiteBalance?: number | string,
59 autoFocus?: string | boolean | number,
60 pictureSize?: string,
61 onMountError?: MountErrorNativeEventType => void,
62 barCodeScannerSettings?: {},
63 onBarCodeScanned?: ({ type: string, data: string }) => void,
64 faceDetectorSettings?: {},
65 onFacesDetected?: ({ faces: Array<*> }) => void,
66};
67
68const CameraManager: Object =
69 NativeModulesProxy.ExponentCameraManager || NativeModulesProxy.ExponentCameraModule;
70
71const EventThrottleMs = 500;
72
73const _PICTURE_SAVED_CALLBACKS = {};
74let _GLOBAL_PICTURE_ID = 1;
75
76export default class Camera extends React.Component<PropsType> {
77 static Constants = {
78 Type: CameraManager.Type,
79 FlashMode: CameraManager.FlashMode,
80 AutoFocus: CameraManager.AutoFocus,
81 WhiteBalance: CameraManager.WhiteBalance,
82 VideoQuality: CameraManager.VideoQuality,
83 };
84
85 // Values under keys from this object will be transformed to native options
86 static ConversionTables = {
87 type: CameraManager.Type,
88 flashMode: CameraManager.FlashMode,
89 autoFocus: CameraManager.AutoFocus,
90 whiteBalance: CameraManager.WhiteBalance,
91 };
92
93 static propTypes = {
94 ...ViewPropTypes,
95 zoom: PropTypes.number,
96 ratio: PropTypes.string,
97 focusDepth: PropTypes.number,
98 onMountError: PropTypes.func,
99 pictureSize: PropTypes.string,
100 onCameraReady: PropTypes.func,
101 useCamera2Api: PropTypes.bool,
102 onBarCodeScanned: PropTypes.func,
103 barCodeScannerSettings: PropTypes.object,
104 onFacesDetected: PropTypes.func,
105 faceDetectorSettings: PropTypes.object,
106 type: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
107 flashMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
108 whiteBalance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
109 autoFocus: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
110 };
111
112 static defaultProps: Object = {
113 zoom: 0,
114 ratio: '4:3',
115 focusDepth: 0,
116 faceDetectorSettings: {},
117 type: CameraManager.Type.back,
118 autoFocus: CameraManager.AutoFocus.on,
119 flashMode: CameraManager.FlashMode.off,
120 whiteBalance: CameraManager.WhiteBalance.auto,
121 };
122
123 _cameraRef: ?Object;
124 _cameraHandle: ?number;
125 _lastEvents: { [string]: string };
126 _lastEventsTimes: { [string]: Date };
127
128 constructor(props: PropsType) {
129 super(props);
130 this._lastEvents = {};
131 this._lastEventsTimes = {};
132 }
133
134 async takePictureAsync(options?: PictureOptions): Promise<CapturedPicture> {
135 if (!options) {
136 options = {};
137 }
138 if (!options.quality) {
139 options.quality = 1;
140 }
141 if (options.onPictureSaved) {
142 const id = _GLOBAL_PICTURE_ID++;
143 _PICTURE_SAVED_CALLBACKS[id] = options.onPictureSaved;
144 options.id = id;
145 options.fastMode = true;
146 }
147 return await CameraManager.takePicture(options, this._cameraHandle);
148 }
149
150 async getSupportedRatiosAsync(): Promise<Array<string>> {
151 if (Platform.OS === 'android') {
152 return await CameraManager.getSupportedRatios(this._cameraHandle);
153 } else {
154 throw new Error('Ratio is not supported on iOS');
155 }
156 }
157
158 async getAvailablePictureSizesAsync(ratio?: string): Promise<Array<string>> {
159 return await CameraManager.getAvailablePictureSizes(ratio, this._cameraHandle);
160 }
161
162 async recordAsync(options?: RecordingOptions): Promise<RecordingResult> {
163 if (!options || typeof options !== 'object') {
164 options = {};
165 } else if (typeof options.quality === 'string') {
166 options.quality = Camera.Constants.VideoQuality[options.quality];
167 }
168 return await CameraManager.record(options, this._cameraHandle);
169 }
170
171 stopRecording() {
172 CameraManager.stopRecording(this._cameraHandle);
173 }
174
175 pausePreview() {
176 CameraManager.pausePreview(this._cameraHandle);
177 }
178
179 resumePreview() {
180 CameraManager.resumePreview(this._cameraHandle);
181 }
182
183 _onCameraReady = () => {
184 if (this.props.onCameraReady) {
185 this.props.onCameraReady();
186 }
187 };
188
189 _onMountError = ({ nativeEvent }: { nativeEvent: MountErrorNativeEventType }) => {
190 if (this.props.onMountError) {
191 this.props.onMountError(nativeEvent);
192 }
193 };
194
195 _onPictureSaved = ({ nativeEvent }: { nativeEvent: PictureSavedNativeEventType }) => {
196 const callback = _PICTURE_SAVED_CALLBACKS[nativeEvent.id];
197 if (callback) {
198 callback(nativeEvent.data);
199 delete _PICTURE_SAVED_CALLBACKS[nativeEvent.id];
200 }
201 };
202
203 _onObjectDetected = (callback: ?Function) => ({ nativeEvent }: EventCallbackArgumentsType) => {
204 const { type } = nativeEvent;
205 if (
206 this._lastEvents[type] &&
207 this._lastEventsTimes[type] &&
208 JSON.stringify(nativeEvent) === this._lastEvents[type] &&
209 new Date() - this._lastEventsTimes[type] < EventThrottleMs
210 ) {
211 return;
212 }
213
214 if (callback) {
215 callback(nativeEvent);
216 this._lastEventsTimes[type] = new Date();
217 this._lastEvents[type] = JSON.stringify(nativeEvent);
218 }
219 };
220
221 _setReference = (ref: ?Object) => {
222 if (ref) {
223 this._cameraRef = ref;
224 this._cameraHandle = findNodeHandle(ref);
225 } else {
226 this._cameraRef = null;
227 this._cameraHandle = null;
228 }
229 };
230
231 _onBarCodeScanned = () => {
232 const onBarCodeRead = this.props.onBarCodeRead && ((data) => {
233 console.warn("'onBarCodeRead' is deprecated in favour of 'onBarCodeScanned'");
234 return this.props.onBarCodeRead(data);
235 });
236 return this.props.onBarCodeScanned || onBarCodeRead;
237 };
238
239 render() {
240 const nativeProps = this._convertNativeProps(this.props);
241
242 return (
243 <ExponentCamera
244 {...nativeProps}
245 ref={this._setReference}
246 onCameraReady={this._onCameraReady}
247 onMountError={this._onMountError}
248 onPictureSaved={this._onPictureSaved}
249 onBarCodeScanned={this._onObjectDetected(this._onBarCodeScanned())}
250 onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
251 />
252 );
253 }
254
255 _convertNativeProps(props: PropsType) {
256 const newProps = mapValues(props, this._convertProp);
257
258 const propsKeys = Object.keys(newProps);
259 if (!propsKeys.includes("barCodeScannerSettings") && propsKeys.includes("barCodeTypes")) { // barCodeTypes is deprecated
260 newProps.barCodeScannerSettings = {
261 barCodeTypes: newProps.barCodeTypes,
262 };
263 }
264
265 if (props.onBarCodeScanned || props.onBarCodeRead) { // onBarCodeRead is deprecated
266 newProps.barCodeScannerEnabled = true;
267 }
268
269 if (props.onFacesDetected) {
270 newProps.faceDetectorEnabled = true;
271 }
272
273 if (Platform.OS === 'ios') {
274 delete newProps.ratio;
275 delete newProps.useCamera2Api;
276 }
277
278 return newProps;
279 }
280
281 _convertProp(value: *, key: string): * {
282 if (typeof value === 'string' && Camera.ConversionTables[key]) {
283 return Camera.ConversionTables[key][value];
284 }
285
286 return value;
287 }
288}
289
290export const Constants = Camera.Constants;
291
292const ExponentCamera = requireNativeViewManager('ExponentCamera', Camera);