diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2f8c00cf..d667ec7be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,9 @@
[**THE CHANGELOG OF FURTHER VERSIONS (STARTING WITH 1.4.0) IS MAINTAINED WITH GITHUB RELEASES AND CAN BE FOUND HERE**](https://github.com/react-native-community/react-native-camera/releases)
+
+#### 4.0.0-beta.1
+
+* Adapt to the RN0.84 framework and upgrade the baseline to 4.2.1
+
#### 3.42.0
* release version 3.42.0
diff --git a/COMMITTERS.md b/COMMITTERS.md
new file mode 100644
index 000000000..c2ee75d65
--- /dev/null
+++ b/COMMITTERS.md
@@ -0,0 +1,7 @@
+## Committers列表
+
+### 以下是此项目的committer人员
+
+不区分先后顺序
+
+- [xiafeng_xf_admin](https://gitee.com/xiafeng_xf_admin)
\ No newline at end of file
diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 000000000..3b9f3f8a0
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,75 @@
+
+
+
react-native-camera +
react-native-camera + Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 | + + +1x + +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x + + + + + + +1x + + + +1x + | // @flow +import NativeTurboModule from './NativeFaceDetector'; + +const faceDetectionDisabledMessage = 'Face detection has not been included in this build.'; + +const FaceDetectorModule: Object = NativeTurboModule || { + stubbed: true, + Mode: {}, + Landmarks: {}, + Classifications: {}, + detectFaces: () => new Promise((_, reject) => reject(faceDetectionDisabledMessage)), +}; + +type Point = { x: number, y: number }; + +export type FaceFeature = { + bounds: { + size: { + width: number, + height: number, + }, + origin: Point, + }, + smilingProbability?: number, + leftEarPosition?: Point, + rightEarPosition?: Point, + leftEyePosition?: Point, + leftEyeOpenProbability?: number, + rightEyePosition?: Point, + rightEyeOpenProbability?: number, + leftCheekPosition?: Point, + rightCheekPosition?: Point, + leftMouthPosition?: Point, + mouthPosition?: Point, + rightMouthPosition?: Point, + bottomMouthPosition?: Point, + noseBasePosition?: Point, + yawAngle?: number, + rollAngle?: number, +}; + +type DetectionOptions = { + mode?: $Keys<typeof FaceDetectorModule.Mode>, + detectLandmarks?: $Keys<typeof FaceDetectorModule.Landmarks>, + runClassifications?: $Keys<typeof FaceDetectorModule.Classifications>, +}; + +export default class FaceDetector { + static Constants = { + Mode: FaceDetectorModule.Mode, + Landmarks: FaceDetectorModule.Landmarks, + Classifications: FaceDetectorModule.Classifications, + }; + + static detectFacesAsync(uri: string, options: ?DetectionOptions): Promise<Array<FaceFeature>> { + return FaceDetectorModule.detectFaces({ ...options, uri }); + } +} + +export const Constants = FaceDetector.Constants; + |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +1030 +1031 +1032 +1033 +1034 +1035 +1036 +1037 +1038 +1039 +1040 +1041 +1042 +1043 +1044 +1045 +1046 +1047 +1048 +1049 +1050 +1051 +1052 +1053 +1054 +1055 +1056 +1057 +1058 +1059 +1060 | + + + + + + + + + + + + + + + + + +4x + + + + + + + +4x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4x + + + + + + + + + +4x + + + + + +4x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4x + +4x + + + + + + + + +4x + + + + + + + + + + + + + + + + + + + + + + + + +4x + + + + + + + + + + + + + +4x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +12x + + + + + + +12x +12x +12x +12x +12x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2x + +2x +1x + +1x + + + + +1x + +1x + +1x +1x +1x +1x +1x + + + + + + + + + + +2x + +2x +1x + +1x + + + +12x +2x + +2x +1x + +1x + + + +12x + +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +12x + + + + + +12x + + + + + +12x + + + + +12x +3x +1x + +3x +1x + + +12x + + + + + +12x + + + + + + + + +12x + + + + + +12x +5x +5x + + + + + +1x + + +4x +3x +3x +3x + + + +12x + + + + + +12x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +12x + + + + + + + + +12x + +12x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4x + + +2x +2x + + + + | // @flow
+import React from 'react';
+import PropTypes from 'prop-types';
+import NativeCamera, { Commands, RecordResponse } from "./NativeCamera";
+import NativeCameraModule from "./NativeCameraModule";
+import { FaceFeature } from './FaceDetector';
+import {
+ findNodeHandle,
+ Platform,
+ ViewPropTypes,
+ View,
+ ActivityIndicator,
+ Text,
+ StyleSheet,
+ PermissionsAndroid,
+ DeviceEventEmitter
+} from 'react-native';
+
+const Rationale = PropTypes.shape({
+ title: PropTypes.string.isRequired,
+ message: PropTypes.string.isRequired,
+ buttonPositive: PropTypes.string,
+ buttonNegative: PropTypes.string,
+ buttonNeutral: PropTypes.string,
+});
+
+const requestPermissions = async (
+ captureAudio: boolean,
+ CameraManager: any,
+ androidCameraPermissionOptions: Rationale | null,
+ androidRecordAudioPermissionOptions: Rationale | null,
+): Promise<{ hasCameraPermissions: boolean, hasRecordAudioPermissions: boolean }> => {
+ let hasCameraPermissions = false;
+ let hasRecordAudioPermissions = false;
+ if (Platform.OS === 'ios') {
+ hasCameraPermissions = await CameraManager.checkVideoAuthorizationStatus();
+ } else if (Platform.OS === 'android') {
+ const cameraPermissionResult = await PermissionsAndroid.request(
+ PermissionsAndroid.PERMISSIONS.CAMERA,
+ androidCameraPermissionOptions,
+ );
+ if (typeof cameraPermissionResult === 'boolean') {
+ hasCameraPermissions = cameraPermissionResult;
+ } else {
+ hasCameraPermissions = cameraPermissionResult === PermissionsAndroid.RESULTS.GRANTED;
+ }
+ } else if (Platform.OS === 'windows') {
+ hasCameraPermissions = await CameraManager.checkMediaCapturePermission();
+ } else if (Platform.OS === 'harmony') {
+ hasCameraPermissions = await NativeCameraModule.getCameraPermission();
+ }
+
+ if (captureAudio) {
+ if (Platform.OS === 'ios') {
+ hasRecordAudioPermissions = await CameraManager.checkRecordAudioAuthorizationStatus();
+ } else if (Platform.OS === 'android') {
+ if (await CameraManager.checkIfRecordAudioPermissionsAreDefined()) {
+ const audioPermissionResult = await PermissionsAndroid.request(
+ PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
+ androidRecordAudioPermissionOptions,
+ );
+ if (typeof audioPermissionResult === 'boolean') {
+ hasRecordAudioPermissions = audioPermissionResult;
+ } else {
+ hasRecordAudioPermissions = audioPermissionResult === PermissionsAndroid.RESULTS.GRANTED;
+ }
+ } else if (__DEV__) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ `The 'captureAudio' property set on RNCamera instance but 'RECORD_AUDIO' permissions not defined in the applications 'AndroidManifest.xml'. ` +
+ `If you want to record audio you will have to add '<uses-permission android:name="android.permission.RECORD_AUDIO"/>' to your 'AndroidManifest.xml'. ` +
+ `Otherwise you should set the 'captureAudio' property on the component instance to 'false'.`,
+ );
+ }
+ } else if (Platform.OS === 'windows') {
+ hasRecordAudioPermissions = await CameraManager.checkMediaCapturePermission();
+ } else if (Platform.OS === 'harmony') {
+ hasRecordAudioPermissions = await NativeCameraModule.getAuidPermission();
+ }
+ }
+
+ return {
+ hasCameraPermissions,
+ hasRecordAudioPermissions,
+ };
+};
+
+const styles = StyleSheet.create({
+ authorizationContainer: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ notAuthorizedText: {
+ textAlign: 'center',
+ fontSize: 16,
+ },
+});
+
+type Orientation = 'auto' | 'landscapeLeft' | 'landscapeRight' | 'portrait' | 'portraitUpsideDown';
+type OrientationNumber = 1 | 2 | 3 | 4;
+
+type PictureOptions = {
+ quality?: number,
+ orientation?: Orientation | OrientationNumber,
+ base64?: boolean,
+ mirrorImage?: boolean,
+ exif?: boolean,
+ writeExif?: boolean | { [name: string]: any },
+ width?: number,
+ fixOrientation?: boolean,
+ forceUpOrientation?: boolean,
+ pauseAfterCapture?: boolean,
+ imageType?: 'jpeg' | 'png',
+ path?: string,
+};
+
+type TrackedFaceFeature = FaceFeature & {
+ faceID?: number,
+};
+
+type TrackedTextFeature = {
+ type: string,
+ bounds: {
+ size: {
+ width: number,
+ height: number,
+ },
+ origin: {
+ x: number,
+ y: number,
+ },
+ },
+ value: string,
+ components: Array<TrackedTextFeature>,
+};
+
+type TrackedBarcodeFeature = {
+ bounds: {
+ size: {
+ width: number,
+ height: number,
+ },
+ origin: {
+ x: number,
+ y: number,
+ },
+ },
+ data: string,
+ dataRaw: string,
+ type: BarcodeType,
+ format?: string,
+ addresses?: {
+ addressesType?: 'UNKNOWN' | 'Work' | 'Home',
+ addressLines?: string[],
+ }[],
+ emails?: Email[],
+ phones?: Phone[],
+ urls: ?(string[]),
+ name?: {
+ firstName?: string,
+ lastName?: string,
+ middleName?: string,
+ prefix?: string,
+ pronounciation?: string,
+ suffix?: string,
+ formattedName?: string,
+ },
+ phone?: Phone,
+ organization?: string,
+ latitude?: number,
+ longitude?: number,
+ ssid?: string,
+ password?: string,
+ encryptionType?: string,
+ title?: string,
+ url?: string,
+ firstName?: string,
+ middleName?: string,
+ lastName?: string,
+ gender?: string,
+ addressCity?: string,
+ addressState?: string,
+ addressStreet?: string,
+ addressZip?: string,
+ birthDate?: string,
+ documentType?: string,
+ licenseNumber?: string,
+ expiryDate?: string,
+ issuingDate?: string,
+ issuingCountry?: string,
+ eventDescription?: string,
+ location?: string,
+ organizer?: string,
+ status?: string,
+ summary?: string,
+ start?: string,
+ end?: string,
+ email?: Email,
+ phoneNumber?: string,
+ message?: string,
+};
+
+type BarcodeType =
+ | 'EMAIL'
+ | 'PHONE'
+ | 'CALENDAR_EVENT'
+ | 'DRIVER_LICENSE'
+ | 'GEO'
+ | 'SMS'
+ | 'CONTACT_INFO'
+ | 'WIFI'
+ | 'TEXT'
+ | 'ISBN'
+ | 'PRODUCT'
+ | 'URL';
+
+type Email = {
+ address?: string,
+ body?: string,
+ subject?: string,
+ emailType?: 'UNKNOWN' | 'Work' | 'Home',
+};
+
+type Phone = {
+ number?: string,
+ phoneType?: 'UNKNOWN' | 'Work' | 'Home' | 'Fax' | 'Mobile',
+};
+
+type ImageType = {
+ jpeg: any,
+ png: any,
+};
+
+type HardwareCamera = {
+ deviceType?: string,
+ id: string,
+ type: number,
+};
+
+type RecordingOptions = {
+ maxDuration?: number,
+ maxFileSize?: number,
+ orientation?: Orientation,
+ quality?: number | string,
+ fps?: number,
+ codec?: string,
+ mute?: boolean,
+ path?: string,
+ videoBitrate?: number,
+};
+
+type EventCallbackArgumentsType = {
+ nativeEvent: Object,
+};
+
+type Rect = {
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+};
+
+type PropsType = typeof View.props & {
+ zoom?: number,
+ useNativeZoom?: boolean,
+ maxZoom?: number,
+ ratio?: string,
+ focusDepth?: number,
+ type?: number | string,
+ onCameraReady?: Function,
+ onAudioInterrupted?: Function,
+ onAudioConnected?: Function,
+ onStatusChange?: Function,
+ onBarCodeRead?: Function,
+ onPictureTaken?: Function,
+ onPictureSaved?: Function,
+ onRecordingStart?: Function,
+ onRecordingEnd?: Function,
+ onTap?: Function,
+ onDoubleTap?: Function,
+ onGoogleVisionBarcodesDetected?: ({ barcodes: Array<TrackedBarcodeFeature> }) => void,
+ onSubjectAreaChanged?: ({ nativeEvent: { prevPoint: {| x: number, y: number |} } }) => void,
+ faceDetectionMode ?: number,
+ trackingEnabled ?: boolean,
+ flashMode ?: number | string,
+ exposure ?: number,
+ barCodeTypes ?: Array < string >,
+ googleVisionBarcodeType ?: number,
+ googleVisionBarcodeMode ?: number,
+ whiteBalance ?: number | string | { temperature: number, tint: number, redGainOffset?: number, greenGainOffset?: number, blueGainOffset?: number },
+ faceDetectionLandmarks ?: number,
+ autoFocus ?: string | boolean | number,
+ autoFocusPointOfInterest ?: { x: number, y: number },
+ faceDetectionClassifications ?: number,
+ onFacesDetected ?: ({ faces: Array < TrackedFaceFeature > }) => void,
+ onTextRecognized ?: ({ textBlocks: Array < TrackedTextFeature > }) => void,
+ captureAudio ?: boolean,
+ keepAudioSession ?: boolean,
+ useCamera2Api ?: boolean,
+ playSoundOnCapture ?: boolean,
+ playSoundOnRecord ?: boolean,
+ videoStabilizationMode ?: number | string,
+ pictureSize ?: string,
+ cameraId ?: string,
+ rectOfInterest: Rect,
+};
+
+type StateType = {
+ isAuthorized: boolean,
+ isAuthorizationChecked: boolean,
+ recordAudioPermissionStatus: RecordAudioPermissionStatus,
+};
+
+export type Status = 'READY' | 'PENDING_AUTHORIZATION' | 'NOT_AUTHORIZED';
+
+const CameraStatus: { [key: Status]: Status } = {
+ READY: 'READY',
+ PENDING_AUTHORIZATION: 'PENDING_AUTHORIZATION',
+ NOT_AUTHORIZED: 'NOT_AUTHORIZED',
+};
+
+export type RecordAudioPermissionStatus = 'AUTHORIZED' | 'NOT_AUTHORIZED' | 'PENDING_AUTHORIZATION';
+
+const RecordAudioPermissionStatusEnum: {
+ [key: RecordAudioPermissionStatus]: RecordAudioPermissionStatus,
+} = {
+ AUTHORIZED: 'AUTHORIZED',
+ PENDING_AUTHORIZATION: 'PENDING_AUTHORIZATION',
+ NOT_AUTHORIZED: 'NOT_AUTHORIZED',
+};
+
+const CameraManager: Object = {
+ stubbed: true,
+ Type: {
+ back: 'back',
+ front: 'front',
+ },
+ AutoFocus: {
+ on: 'on',
+ off: 'off',
+ },
+ FlashMode: {
+ off: 'off',
+ on: 'on',
+ torch: 'torch',
+ auto: 'auto',
+ },
+ WhiteBalance: {
+ sunny: 'sunny',
+ cloudy: 'cloudy',
+ shadow: 'shadow',
+ incandescent: 'incandescent',
+ fluorescent: 'fluorescent',
+ auto: 'auto',
+ },
+ BarCodeType: {},
+ FaceDetection: {
+ fast: 1,
+ Mode: {},
+ Landmarks: {
+ none: 0,
+ },
+ Classifications: {
+ none: 0,
+ },
+ },
+ GoogleVisionBarcodeDetection: {
+ BarcodeType: 0,
+ BarcodeMode: 0,
+ },
+ VideoQuality: {
+ '2160p': 1,
+ '1080p': 2,
+ '720p': 3,
+ '480p': 4,
+ '4:3': 5,
+ },
+ ImageType: {
+ jpeg: 1,
+ png: 2,
+ },
+ VideoCodec: {},
+ CaptureTarget: {
+ disk: 0,
+ },
+ VideoStabilization: {},
+ Orientation: {
+ auto: 'auto',
+ landscapeLeft: 'landscapeLeft',
+ landscapeRight: 'landscapeRight',
+ portrait: 'portrait',
+ portraitUpsideDown: 'portraitUpsideDown',
+ },
+};
+
+const EventThrottleMs = 500;
+
+const mapValues = (input, mapper) => {
+ const result = {};
+ Object.entries(input).map(([key, value]) => {
+ result[key] = mapper(value, key);
+ });
+ return result;
+};
+
+export default class Camera extends React.Component<PropsType, StateType> {
+ static Constants = {
+ Type: CameraManager.Type,
+ FlashMode: CameraManager.FlashMode,
+ AutoFocus: CameraManager.AutoFocus,
+ WhiteBalance: CameraManager.WhiteBalance,
+ VideoQuality: CameraManager.VideoQuality,
+ ImageType: CameraManager.ImageType,
+ VideoCodec: CameraManager.VideoCodec,
+ BarCodeType: CameraManager.BarCodeType,
+ GoogleVisionBarcodeDetection: CameraManager.GoogleVisionBarcodeDetection,
+ FaceDetection: CameraManager.FaceDetection,
+ CameraStatus,
+ CaptureTarget: CameraManager.CaptureTarget,
+ RecordAudioPermissionStatus: RecordAudioPermissionStatusEnum,
+ VideoStabilization: CameraManager.VideoStabilization,
+ Orientation: {
+ auto: 'auto',
+ landscapeLeft: 'landscapeLeft',
+ landscapeRight: 'landscapeRight',
+ portrait: 'portrait',
+ portraitUpsideDown: 'portraitUpsideDown',
+ },
+ };
+
+ // Values under keys from this object will be transformed to native options
+ static ConversionTables = {
+ type: CameraManager.Type,
+ flashMode: CameraManager.FlashMode,
+ exposure: CameraManager.Exposure,
+ autoFocus: CameraManager.AutoFocus,
+ whiteBalance: CameraManager.WhiteBalance,
+ faceDetectionMode: (CameraManager.FaceDetection || {}).Mode,
+ faceDetectionLandmarks: (CameraManager.FaceDetection || {}).Landmarks,
+ faceDetectionClassifications: (CameraManager.FaceDetection || {}).Classifications,
+ googleVisionBarcodeType: (CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType,
+ googleVisionBarcodeMode: (CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeMode,
+ videoStabilizationMode: CameraManager.VideoStabilization || {},
+ };
+
+ static propTypes = {
+ ...ViewPropTypes,
+ zoom: PropTypes.number,
+ useNativeZoom: PropTypes.bool,
+ maxZoom: PropTypes.number,
+ ratio: PropTypes.string,
+ focusDepth: PropTypes.number,
+ onMountError: PropTypes.func,
+ onCameraReady: PropTypes.func,
+ onAudioInterrupted: PropTypes.func,
+ onAudioConnected: PropTypes.func,
+ onStatusChange: PropTypes.func,
+ onBarCodeRead: PropTypes.func,
+ onPictureTaken: PropTypes.func,
+ onPictureSaved: PropTypes.func,
+ onRecordingStart: PropTypes.func,
+ onRecordingEnd: PropTypes.func,
+ onTap: PropTypes.func,
+ onDoubleTap: PropTypes.func,
+ onGoogleVisionBarcodesDetected: PropTypes.func,
+ onFacesDetected: PropTypes.func,
+ onTextRecognized: PropTypes.func,
+ onSubjectAreaChanged: PropTypes.func,
+ trackingEnabled: PropTypes.bool,
+ faceDetectionMode: PropTypes.number,
+ faceDetectionLandmarks: PropTypes.number,
+ faceDetectionClassifications: PropTypes.number,
+ barCodeTypes: PropTypes.arrayOf(PropTypes.string),
+ googleVisionBarcodeType: PropTypes.number,
+ googleVisionBarcodeMode: PropTypes.number,
+ type: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ cameraId: PropTypes.string,
+ flashMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ exposure: PropTypes.number,
+ whiteBalance: PropTypes.oneOfType([PropTypes.string, PropTypes.number,
+ PropTypes.shape({
+ temperature: PropTypes.number, tint: PropTypes.number,
+ redGainOffset: PropTypes.number,
+ greenGainOffset: PropTypes.number,
+ blueGainOffset: PropTypes.number
+ })]),
+ autoFocus: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
+ autoFocusPointOfInterest: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }),
+ permissionDialogTitle: PropTypes.string,
+ permissionDialogMessage: PropTypes.string,
+ androidCameraPermissionOptions: Rationale,
+ androidRecordAudioPermissionOptions: Rationale,
+ notAuthorizedView: PropTypes.element,
+ pendingAuthorizationView: PropTypes.element,
+ captureAudio: PropTypes.bool,
+ keepAudioSession: PropTypes.bool,
+ useCamera2Api: PropTypes.bool,
+ playSoundOnCapture: PropTypes.bool,
+ playSoundOnRecord: PropTypes.bool,
+ videoStabilizationMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ pictureSize: PropTypes.string,
+ mirrorVideo: PropTypes.bool,
+ rectOfInterest: PropTypes.any,
+ defaultVideoQuality: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ };
+
+
+ static defaultProps: Object = {
+ useNativeZoom: false,
+ ratio: '4:3',
+ focusDepth: 0,
+ type: "back",
+ cameraId: '',
+ autoFocus: CameraManager.AutoFocus.on,
+ flashMode: CameraManager.FlashMode.off,
+ whiteBalance: CameraManager.WhiteBalance.auto,
+ faceDetectionMode: (CameraManager.FaceDetection || {}).fast,
+ barCodeTypes: Object.values(CameraManager.BarCodeType),
+ googleVisionBarcodeType: ((CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType || {})
+ .None,
+ googleVisionBarcodeMode: ((CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeMode || {})
+ .NORMAL,
+ faceDetectionLandmarks: ((CameraManager.FaceDetection || {}).Landmarks || {}).none,
+ faceDetectionClassifications: ((CameraManager.FaceDetection || {}).Classifications || {}).none,
+ permissionDialogTitle: '',
+ permissionDialogMessage: '',
+ androidCameraPermissionOptions: null,
+ androidRecordAudioPermissionOptions: null,
+ notAuthorizedView: (
+ <View style={styles.authorizationContainer}>
+ <Text style={styles.notAuthorizedText}>Camera not authorized</Text>
+ </View>
+ ),
+ pendingAuthorizationView: (
+ <View style={styles.authorizationContainer}>
+ <ActivityIndicator size="small" />
+ </View>
+ ),
+ captureAudio: true,
+ keepAudioSession: false,
+ useCamera2Api: false,
+ playSoundOnCapture: false,
+ playSoundOnRecord: false,
+ pictureSize: 'None',
+ videoStabilizationMode: 'off', // harmony: cpp 枚举按字符串解析("off"/"standard"/"cinematic"/"auto")
+ mirrorVideo: false,
+ };
+
+ _cameraRef: React.ElementRef<typeof NativeCamera> | null = null
+ _cameraHandle: ?number;
+ _lastEvents: { [string]: string };
+ _lastEventsTimes: { [string]: Date };
+ _isMounted: boolean;
+
+ constructor(props: PropsType) {
+ super(props);
+ this._lastEvents = {};
+ this._lastEventsTimes = {};
+ this._isMounted = true;
+ this.state = {
+ isAuthorized: false,
+ isAuthorizationChecked: false,
+ recordAudioPermissionStatus: RecordAudioPermissionStatusEnum.PENDING_AUTHORIZATION,
+ };
+ }
+
+ async takePictureAsync(options?: PictureOptions) {
+ if (!options) {
+ options = {};
+ }
+ if (!options.quality) {
+ options.quality = 1;
+ }
+
+ if (options.orientation) {
+ if (typeof options.orientation !== 'number') {
+ const { orientation } = options;
+ options.orientation = CameraManager.Orientation[orientation];
+ if (__DEV__) {
+ if (typeof options.orientation !== 'number') {
+ // eslint-disable-next-line no-console
+ console.warn(`Orientation '${orientation}' is invalid.`);
+ }
+ }
+ }
+ }
+
+ if (options.pauseAfterCapture === undefined) {
+ options.pauseAfterCapture = false;
+ }
+
+ if (!this._cameraHandle) {
+ throw 'Camera handle cannot be null';
+ }
+
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+
+ return new Promise((resolve, reject) => {
+ try {
+ const onCodeScannedListener = DeviceEventEmitter.addListener('onTaskPhoto', (data) => {
+ resolve(data);
+ onCodeScannedListener.remove();
+ });
+
+ Commands.takePicture(this._cameraRef, options)
+ }
+ catch (e) {
+ reject(e);
+ return;
+ }
+ });
+ }
+
+ async getSupportedRatiosAsync() {
+ Iif (Platform.OS === 'android') {
+ return await Commands.getSupportedRatios(this._cameraHandle);
+ } else if (Platform.OS === 'harmony') {
+ return await NativeCameraModule.getSupportedRatios(this._cameraHandle);
+ } else {
+ throw new Error('Ratio is not supported on iOS');
+ }
+ }
+
+ async getCameraIdsAsync() {
+ Iif (Platform.OS === 'android') {
+ return await CameraManager.getCameraIds(this._cameraHandle);
+ } else Iif (Platform.OS === 'ios') {
+ return await CameraManager.getCameraIds(); // iOS does not need a camera instance
+ } else Eif (Platform.OS === 'harmony') {
+ return new Promise(async (resolve, reject) => {
+ try {
+ let data = await NativeCameraModule.getCameraIds();
+ resolve(data)
+ }
+ catch (e) {
+ reject(e);
+ return;
+ }
+ });;
+ }
+ }
+
+ static async checkIfVideoIsValid(path) {
+ Iif (Platform.OS === 'android') {
+ return await CameraManager.checkIfVideoIsValid(path);
+ } else if (Platform.OS === 'harmony') {
+ return await NativeCameraModule.checkIfVideoIsValid(path);
+ } else {
+ return true; // iOS: not implemented
+ }
+ }
+
+ getSupportedPreviewFpsRange = async (): Promise<[]> => {
+ Iif (Platform.OS === 'android') {
+ return await Commands.getSupportedPreviewFpsRange(this._cameraHandle);
+ } else if (Platform.OS === 'harmony') {
+ return await NativeCameraModule.getSupportedPreviewFpsRange(this._cameraHandle);
+ } else {
+ throw new Error('getSupportedPreviewFpsRange is not supported on iOS');
+ }
+ };
+
+ getAvailablePictureSizes = async (): string[] => {
+ //$FlowFixMe
+ return await NativeCameraModule.getAvailablePictureSizes();
+ };
+
+ async recordAsync(options?: RecordingOptions) {
+ if (!options || typeof options !== 'object') {
+ options = {};
+ } else if (typeof options.quality === 'string') {
+ options.quality = Camera.Constants.VideoQuality[options.quality];
+ }
+ if (options.orientation) {
+ if (typeof options.orientation !== 'number') {
+ const { orientation } = options;
+ options.orientation = CameraManager.Orientation[orientation];
+ if (__DEV__) {
+ if (typeof options.orientation !== 'number') {
+ // eslint-disable-next-line no-console
+ console.warn(`Orientation '${orientation}' is invalid.`);
+ }
+ }
+ }
+ }
+
+ if (__DEV__) {
+ if (options.videoBitrate && typeof options.videoBitrate !== 'number') {
+ // eslint-disable-next-line no-console
+ console.warn('Video Bitrate should be a positive integer');
+ }
+ }
+
+ const { recordAudioPermissionStatus } = this.state;
+ const { captureAudio } = this.props;
+
+ if (
+ !captureAudio ||
+ recordAudioPermissionStatus !== RecordAudioPermissionStatusEnum.AUTHORIZED
+ ) {
+ options.mute = true;
+ }
+
+ if (__DEV__) {
+ if (
+ (!options.mute || captureAudio) &&
+ recordAudioPermissionStatus !== RecordAudioPermissionStatusEnum.AUTHORIZED
+ ) {
+ // eslint-disable-next-line no-console
+ console.warn('Recording with audio not possible. Permissions are missing.');
+ }
+ }
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+
+ return new Promise((resolve, reject) => {
+ try {
+ const onRecordingFinishedListener = DeviceEventEmitter.addListener('onRecordingFinished', (video: RecordResponse) => {
+ resolve(video)
+ onRecordingFinishedListener.remove();
+ });
+ Commands.record(this._cameraRef, options)
+ }
+ catch (e) {
+ reject(e);
+ return;
+ }
+ });
+ }
+
+ stopRecording() {
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+ Commands.stopRecording(this._cameraRef);
+ }
+
+ pauseRecording() {
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+ Commands.pauseRecording(this._cameraRef);
+ }
+
+ resumeRecording() {
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+ Commands.resumeRecording(this._cameraRef);
+ }
+
+ pausePreview() {
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+
+ Commands.pausePreview(this._cameraRef);
+ }
+
+ isRecording() {
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+ return new Promise((resolve, reject) => {
+ try {
+ const isRecordingFinishedListener = DeviceEventEmitter.addListener('isRecording', (isRecording) => {
+ resolve(isRecording)
+ isRecordingFinishedListener.remove();
+ });
+ Commands.isRecording(this._cameraRef)
+ }
+ catch (e) {
+ reject(e);
+ return;
+ }
+ });
+ }
+
+ resumePreview() {
+ if (!this._cameraRef) {
+ throw 'Camera handle cannot be null';
+ }
+ Commands.resumePreview(this._cameraRef);
+ }
+
+ _onMountError = ({ nativeEvent }: EventCallbackArgumentsType) => {
+ if (this.props.onMountError) {
+ this.props.onMountError(nativeEvent);
+ }
+ };
+
+ _onCameraReady = () => {
+ if (this.props.onCameraReady) {
+ this.props.onCameraReady();
+ }
+ };
+
+ _onAudioInterrupted = () => {
+ if (this.props.onAudioInterrupted) {
+ this.props.onAudioInterrupted();
+ }
+ };
+ _onTouch = ({ nativeEvent }: EventCallbackArgumentsType) => {
+ if (this.props.onTap && !nativeEvent.isDoubleTap) {
+ this.props.onTap(nativeEvent.touchOrigin);
+ }
+ if (this.props.onDoubleTap && nativeEvent.isDoubleTap) {
+ this.props.onDoubleTap(nativeEvent.touchOrigin);
+ }
+ };
+ _onAudioConnected = () => {
+ if (this.props.onAudioConnected) {
+ this.props.onAudioConnected();
+ }
+ };
+
+ _onStatusChange = () => {
+ if (this.props.onStatusChange) {
+ this.props.onStatusChange({
+ cameraStatus: this.getStatus(),
+ recordAudioPermissionStatus: this.state.recordAudioPermissionStatus,
+ });
+ }
+ };
+
+ _onPictureSaved = ({ nativeEvent }: EventCallbackArgumentsType) => {
+ if (this.props.onPictureSaved) {
+ this.props.onPictureSaved(nativeEvent);
+ }
+ };
+
+ _onObjectDetected = (callback: ?Function) => ({ nativeEvent }: EventCallbackArgumentsType) => {
+ const { type } = nativeEvent;
+ if (
+ this._lastEvents[type] &&
+ this._lastEventsTimes[type] &&
+ JSON.stringify(nativeEvent) === this._lastEvents[type] &&
+ new Date() - this._lastEventsTimes[type] < EventThrottleMs
+ ) {
+ return;
+ }
+
+ if (callback) {
+ callback(nativeEvent);
+ this._lastEventsTimes[type] = new Date();
+ this._lastEvents[type] = JSON.stringify(nativeEvent);
+ }
+ };
+
+ _onSubjectAreaChanged = e => {
+ if (this.props.onSubjectAreaChanged) {
+ this.props.onSubjectAreaChanged(e);
+ }
+ };
+
+ _setReference = (ref: ?Object) => {
+ if (ref) {
+ this._cameraRef = ref;
+ this._cameraHandle = findNodeHandle(ref);
+ } else {
+ this._cameraRef = null;
+ this._cameraHandle = null;
+ }
+ };
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ async arePermissionsGranted() {
+ const {
+ permissionDialogTitle,
+ permissionDialogMessage,
+ androidCameraPermissionOptions,
+ androidRecordAudioPermissionOptions,
+ } = this.props;
+
+ let cameraPermissions = androidCameraPermissionOptions;
+ let audioPermissions = androidRecordAudioPermissionOptions;
+ if (permissionDialogTitle || permissionDialogMessage) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ 'permissionDialogTitle and permissionDialogMessage are deprecated. Please use androidCameraPermissionOptions instead.',
+ );
+ cameraPermissions = {
+ ...cameraPermissions,
+ title: permissionDialogTitle,
+ message: permissionDialogMessage,
+ };
+ audioPermissions = {
+ ...audioPermissions,
+ title: permissionDialogTitle,
+ message: permissionDialogMessage,
+ };
+ }
+
+ const { hasCameraPermissions, hasRecordAudioPermissions } = await requestPermissions(
+ this.props.captureAudio,
+ CameraManager,
+ cameraPermissions,
+ audioPermissions,
+ );
+
+ const recordAudioPermissionStatus = hasRecordAudioPermissions
+ ? RecordAudioPermissionStatusEnum.AUTHORIZED
+ : RecordAudioPermissionStatusEnum.NOT_AUTHORIZED;
+ return { hasCameraPermissions, recordAudioPermissionStatus };
+ }
+
+ async refreshAuthorizationStatus() {
+ const {
+ hasCameraPermissions,
+ recordAudioPermissionStatus,
+ } = await this.arePermissionsGranted();
+ if (this._isMounted === false) {
+ return;
+ }
+
+ this.setState({
+ isAuthorized: hasCameraPermissions,
+ isAuthorizationChecked: true,
+ recordAudioPermissionStatus,
+ });
+ }
+
+ async componentDidMount() {
+ const {
+ hasCameraPermissions,
+ recordAudioPermissionStatus,
+ } = await this.arePermissionsGranted();
+ if (this._isMounted === false) {
+ return;
+ }
+
+ this.setState(
+ {
+ isAuthorized: hasCameraPermissions,
+ isAuthorizationChecked: true,
+ recordAudioPermissionStatus,
+ },
+ this._onStatusChange,
+ );
+ }
+
+ getStatus = (): Status => {
+ const { isAuthorized, isAuthorizationChecked } = this.state;
+ if (isAuthorizationChecked === false) {
+ return CameraStatus.PENDING_AUTHORIZATION;
+ }
+ return isAuthorized ? CameraStatus.READY : CameraStatus.NOT_AUTHORIZED;
+ };
+
+ // FaCC = Function as Child Component;
+ hasFaCC = (): * => typeof this.props.children === 'function';
+
+ renderChildren = (): * => {
+ if (this.hasFaCC()) {
+ return this.props.children({
+ camera: this,
+ status: this.getStatus(),
+ recordAudioPermissionStatus: this.state.recordAudioPermissionStatus,
+ });
+ }
+ return this.props.children;
+ };
+
+ render() {
+ const { style, ...nativeProps } = this._convertNativeProps(this.props);
+
+ if (this.state.isAuthorized || this.hasFaCC()) {
+ return (
+ <View style={style}>
+ <NativeCamera
+ {...nativeProps}
+ videoStabilizationMode = {this.props.videoStabilizationMode}
+ autoFocus={this.props.autoFocus}
+ whiteBalance={this.props.whiteBalance}
+ flashMode={this.props.flashMode}
+ type={this.props.type}
+ style={StyleSheet.absoluteFill}
+ ref={(ref) => {
+ this._cameraRef = ref;
+ this._cameraHandle = findNodeHandle(ref);
+ }}
+ onMountError={this._onMountError}
+ onCameraReady={this._onObjectDetected(this._onCameraReady)}
+ onAudioInterrupted={this._onAudioInterrupted}
+ onAudioConnected={this._onAudioConnected}
+ onGoogleVisionBarcodesDetected={this._onObjectDetected(
+ this.props.onGoogleVisionBarcodesDetected,
+ )}
+ onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)}
+ onTouch={this._onTouch}
+ onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
+ onTextRecognized={this._onObjectDetected(this.props.onTextRecognized)}
+ onPictureSaved={this._onPictureSaved}
+ onSubjectAreaChanged={this._onSubjectAreaChanged}
+ />
+ {this.renderChildren()}
+ </View>
+ );
+ } else if (!this.state.isAuthorizationChecked) {
+ return this.props.pendingAuthorizationView;
+ } else {
+ return this.props.notAuthorizedView;
+ }
+ }
+
+ _convertNativeProps({ children, ...props }: PropsType) {
+ const newProps = mapValues(props, this._convertProp);
+
+ if (props.onBarCodeRead) {
+ newProps.barCodeScannerEnabled = true;
+ }
+
+ if (props.onGoogleVisionBarcodesDetected) {
+ newProps.googleVisionBarcodeDetectorEnabled = true;
+ }
+
+ if (props.onFacesDetected) {
+ newProps.faceDetectorEnabled = true;
+ }
+
+ if (props.onTap || props.onDoubleTap) {
+ newProps.touchDetectorEnabled = true;
+ }
+
+ if (props.onTextRecognized) {
+ newProps.textRecognizerEnabled = true;
+ }
+
+ if (Platform.OS === 'ios') {
+ delete newProps.ratio;
+ }
+
+ return newProps;
+ }
+
+ _convertProp(value: *, key: string): * {
+ if (typeof value === 'string' && Camera.ConversionTables[key]) {
+ return Camera.ConversionTables[key][value];
+ }
+
+ return value;
+ }
+}
+
+export const Constants = Camera.Constants;
+
+export function hasTorch() {
+ Eif (Platform.OS === 'harmony') {
+ return NativeCameraModule.hasTorch();
+ }
+ return CameraManager.hasTorch();
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| FaceDetector.js | +
+
+ |
+ 71.42% | +5/7 | +50% | +1/2 | +33.33% | +1/3 | +83.33% | +5/6 | +
| RNCamera.js | +
+
+ |
+ 26.05% | +68/261 | +21.8% | +46/211 | +23.4% | +11/47 | +25.86% | +67/259 | +
| index.js | +
+
+ |
+ 0% | +0/0 | +0% | +0/0 | +0% | +0/0 | +0% | +0/0 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 | + + + + + + | // @flow
+import RNCamera, { type Status as _CameraStatus, hasTorch } from './RNCamera';
+import FaceDetector from './FaceDetector';
+
+export type CameraStatus = _CameraStatus;
+export { RNCamera, FaceDetector, hasTorch };
+ |