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 @@ + + + + LICENSE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/harmony/reactNativeCamera/README.OpenSource b/README.OpenSource similarity index 72% rename from harmony/reactNativeCamera/README.OpenSource rename to README.OpenSource index c87a179b4..a1ef097c5 100644 --- a/harmony/reactNativeCamera/README.OpenSource +++ b/README.OpenSource @@ -2,9 +2,9 @@ { "Name": "react-native-camera", "License": "MIT License", - "License File": "https://github.com/react-native-camera/react-native-camera/blob/v3.40.0/LICENSE", - "Version Number": "3.40.0", - "Owner" : "react-native-camera" + "License File": "https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE", + "Version Number": "v4.2.1", + "Owner" : "ding_chengjie@hoperun.com", "Upstream URL": "https://github.com/react-native-camera/react-native-camera", "Description": "A Camera component for React Native. Also supports barcode scanning!" } diff --git a/README.md b/README.md index d09eeb1c3..4636a80c1 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,402 @@ -# @react-native-ohos/react-native-camera +> 模板版本:v0.4.2 -This project is based on [react-native-camera](https://github.com/react-native-camera/react-native-camera) +

+

react-native-camera

+

-## Documentation +本项目基于 [react-native-camera](https://github.com/react-native-camera/react-native-camera/tree/v4.2.1) 开发。 -- [中文 ](https://gitcode.com/CPF-RN/usage-docs/blob/master/zh-cn/react-native-camera.md) +请到三方库的 Releases 发布地址查看配套的版本信息:[@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases)。对于未发布到npm的旧版本,请参考[安装指南](./tgz-usage.md)安装tgz包。 -- [英文 ](https://gitcode.com/CPF-RN/usage-docs/blob/master/en/react-native-camera.md) +| 三方库名称 | 三方库版本(npm地址) | 发布信息 | 支持RN版本 | Autolink | 编译API版本 | 社区基线版本 | 源码地址 | +| - | - | - | - | - | - | - | - | +| @react-native-ohos/react-native-camera | [~4.0.0 (开发中)](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.84.* | 是 | API12+ | v4.2.1 | [sig](https://github.com/react-native-oh-library/react-native-camera/tree/sig) | +| @react-native-ohos/react-native-camera | [~3.42.0](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.82.* | 是 | API12+ | v3.40.0 | [br_rnoh0.82](https://github.com/react-native-oh-library/react-native-camera/tree/br_rnoh0.82) | +| @react-native-ohos/react-native-camera | [~3.41.1](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.77.* | 否 | API12+ | v3.40.0 | [br_rnoh0.77](https://github.com/react-native-oh-library/react-native-camera/tree/br_rnoh0.77) | +| @react-native-ohos/react-native-camera | [~3.40.1](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.72.* | 否 | API12+ | v3.40.0 | [br_rnoh0.72](https://github.com/react-native-oh-library/react-native-camera/tree/br_rnoh0.72) | -## License +## 简介 -This library is licensed under [The MIT License (MIT)](https://github.com/react-native-camera/react-native-camera/blob/master/LICENSE) +react-native-camera 是 React Native 的相机组件,同时支持条码扫描。
+本仓库为 `@react-native-ohos/react-native-camera`,当前版本(4.0.0-beta.1)已适配 RN 0.84 框架,并将社区基线升级至 react-native-camera v4.2.1:JS 层采用 codegen spec(NativeCamera / NativeCameraModule / NativeFaceDetector)接入,鸿蒙侧基于 Camera Kit / AVRecorder 实现预览、拍照、录像与扫码能力。 + +## 下载安装 + +进入到工程目录并输入以下命令: + +**npm** + +```bash +npm install @react-native-ohos/react-native-camera +``` + +**yarn** + +```bash +yarn add @react-native-ohos/react-native-camera +``` + +## Link + +| | 是否支持autolink | RN框架版本 | +| - | - | - | +| ~4.0.0 | 是 | 0.84 | + +使用AutoLink的工程需要根据该文档配置,Autolink框架指导文档:https://gitcode.com/openharmony-sig/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md + +如您使用的版本支持 Autolink,并且工程已接入 Autolink,可跳过ManualLink配置。 +
+ ManualLink: 此步骤为手动配置原生依赖项的指导 + +首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 `harmony`。 + +### 1. Overrides RN SDK + +为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 `oh-package.json5` 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。 + +```json +{ + "overrides": { + "@rnoh/react-native-openharmony": "file:./react_native_openharmony.har" + } +} +``` + +### 2. 引入原生端代码 + +方法一:通过 har 包引入(推荐) + +> [!TIP] har 包位于三方库安装路径的 `harmony` 文件夹下。 + +打开 `entry/oh-package.json5`,添加以下依赖 + +```json +"dependencies": { + "@react-native-ohos/react-native-camera": "file:../../node_modules/@react-native-ohos/react-native-camera/harmony/reactNativeCamera.har" + } +``` + +点击右上角的 `sync` 按钮 + +或者在命令行终端执行: + +```bash +cd entry +ohpm install +``` + +方法二:直接链接源码 + +> [!TIP] 如需使用直接链接源码,请参考[直接链接源码说明](https://gitcode.com/CPF-RN/usage-docs/blob/master/zh-cn/link-source-code.md) + +### 3. 配置 CMakeLists 和引入 NativeCameraPackage + +打开 `entry/src/main/cpp/CMakeLists.txt`,添加: + +```diff ++ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") + +# RNOH_BEGIN: manual_package_linking_1 ++ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-camera/src/main/cpp" ./reactNativeCamera) +# RNOH_END: manual_package_linking_1 + +# RNOH_BEGIN: manual_package_linking_2 ++ target_link_libraries(rnoh_app PUBLIC rnoh_native_camera) +# RNOH_END: manual_package_linking_2 +``` + +打开 `entry/src/main/cpp/PackageProvider.cpp`,添加: + +```diff +#include "RNOH/PackageProvider.h" ++ #include "NativeCameraPackage.h" + +using namespace rnoh; + +std::vector> PackageProvider::getPackages(Package::Context ctx) { + return { ++ std::make_shared(ctx) + }; +} +``` + +### 4. 在 ArkTS 侧引入 ReactNativeCameraPackage + +打开 `entry/src/main/ets/RNPackagesFactory.ts`,添加: + +```diff + ... ++ import ReactNativeCameraPackage from '@react-native-ohos/react-native-camera'; + +export function createRNPackages(ctx: RNPackageContext): RNPackage[] { + return [ ++ new ReactNativeCameraPackage(ctx) + ]; +} +``` + +> [!TIP] 本库的 ArkTS 组件(ReactCameraView)由 `ReactNativeCameraPackage` 内部的组件 builder 注册(`createWrappedCustomRNComponentBuilderByComponentNameMap`)自动提供,宿主无需在 `buildCustomRNComponent` 或 `arkTsComponentNames` 中额外配置。 +
+ +### 运行 + +点击右上角的 `sync` 按钮 + +或者在命令行终端执行: + +```bash +cd entry +ohpm install +``` + +然后编译、运行即可。 + +## 约束与限制 + +### 兼容性 + +要使用此库,需要使用正确的 React-Native 和 RNOH 版本。另外,还需要使用配套的 DevEco Studio 和 手机 ROM。 + +在以下版本验证通过: + +1. RNOH: 0.84.2; SDK: HarmonyOS 6.0.1 Release SDK; IDE: DevEco Studio 6.0.1 Release; ROM: 6.0.0.120 SP7; + +### 编译运行API要求 + +> [!TIP] 当前三方库所有版本均已实现版本隔离,支持在 `API12+` 工程编译,及 `API12+` ROM运行。 + +> [!TIP] 以下功能依赖特定版本的API,使用 `低于指定API版本的工程编译` 或 `低于指定API版本的ROM运行` 均可能导致部分功能受限。 +1. `whiteBalance` 属性需要在支持 `API20+` 的工程编译,并在支持 `API20+` 的 ROM 上运行,方可生效。 + +### 权限要求 + +#### 在 entry 目录下的 module.json5 中添加权限 + +打开 `entry/src/main/module.json5`,添加: + +```diff +... +"requestPermissions": [ ++ { ++ "name": "ohos.permission.CAMERA", ++ "reason": "$string:camera_reason", ++ "usedScene": { ++ "abilities": [ ++ "EntryAbility" ++ ], ++ "when":"inuse" ++ } ++ }, ++ { ++ "name": "ohos.permission.MICROPHONE", ++ "reason": "$string:microphone_reason", ++ "usedScene": { ++ "abilities": [ ++ "EntryAbility" ++ ], ++ "when":"inuse" ++ } ++ }, +] +``` + +#### 在 entry 目录下添加申请以上权限的原因 + +打开 `entry/src/main/resources/base/element/string.json`,添加: + +```diff +... +{ + "string": [ ++ { ++ "name": "camera_reason", ++ "value": "使用相机" ++ }, ++ { ++ "name": "microphone_reason", ++ "value": "使用麦克风" ++ }, + ] +} +``` + +## 使用示例 + +下面的代码展示了这个库的基本使用场景: + +> [!WARNING] 使用时 import 的库名不变。 + +```js +import * as React from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { RNCamera } from "@react-native-ohos/react-native-camera"; + +export function CameraExample() { + const cameraRef = React.useRef(null); + + const takePicture = () => { + const camera = cameraRef.current; + if (!camera) { + return; + } + camera + .takePictureAsync({ quality: 0.5 }) + .then((photoResult: any) => { + console.log('takepicture result:' + (photoResult?.path ?? photoResult?.uri)); + }) + .catch((error: unknown) => { + console.warn('takepicture failed:', error); + }); + }; + + return ( + + + + 拍照 + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1 }, + cameraPreview: { width: '100%', aspectRatio: 56 / 100 }, + actionBtn: { + margin: 10, + padding: 10, + backgroundColor: '#1E90FF', + borderRadius: 8, + alignItems: 'center', + }, + actionBtnText: { color: '#fff', fontSize: 16 }, +}); +``` + +## 接口说明 + +> [!TIP] "Platform"列表示该属性在原三方库上支持的平台。 + +> [!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。 + +### RNCamera + +| Name | Description | Type | 函数签名 | Required | Platform | HarmonyOS Support | +| ---- | ----------- | ---- | -------- | -------- |----------|-------------------| +| children | 设置子组件 | View | | no | iOS/Android | yes | +| type | 选择摄像头 | string | | no | iOS/Android | yes | +| cameraId | 精确指定摄像头设备(getCameraIdsAsync 返回的 id) | string | | no | iOS/Android | yes | +| flashMode | 设置闪光灯 | string | | no | iOS/Android | yes | +| exposure | 设置曝光 | number | | no | iOS/Android | yes | +| autoFocus | 设置自动对焦 | string | | no | iOS/Android | yes | +| whiteBalance | 白平衡 | string | | no | iOS/Android | yes | +| captureAudio | 是否开启录音 | boolean | | no | iOS/Android | yes | +| zoom | 设置缩放比例 | number | | no | iOS/Android | yes | +| focusDepth | 非自动对焦时,设置对焦值 | number | | no | iOS/Android | yes | +| maxZoom | 设置最大缩放比例 | number | | no | iOS/Android | yes | +| pictureSize | 设置默认图片分辨率 | string | | no | iOS/Android | yes | +| playSoundOnRecord | 录制开始/结束时播放系统提示音 | boolean | | no | Android | no | +| onCameraReady | 相机就绪回调,无参数 | function | `() => void` | no | iOS/Android | yes | +| onBarCodeRead | 接收 barcode 扫描数据回调;event 含 data(条码内容)、type(码型)、bounds(位置)等 | function | `(event: BarCodeReadEvent) => void` | no | iOS/Android | yes | +| onFacesDetected | 接收人脸识别数据回调;response.faces 为人脸数组(harmony 暂不支持) | function | `(response: {faces: Face[]}) => void` | no | iOS/Android | no | +| onDoubleTap | 双击预览回调;origin 为触点坐标 {x, y}(组件相对坐标) | function | `(origin: Point) => void` | no | iOS/Android | yes | +| notAuthorizedView | 未授权显示的ui | View | | no | Android | no | +| pendingAuthorizationView | 正在授权显示的ui | View | | no | Android | no | +| ratio | 相机比例 | string | | no | Android | no | +| defaultVideoQuality | 默认视频分辨率 | string | | no | iOS/Android | yes | +| autoFocusPointOfInterest | 设置自动对焦点(x/y) | Point | | no | iOS/Android | no | +| useNativeZoom | 启用原生捏合缩放手势 | boolean | | no | iOS/Android | yes | +| onStatusChange | 相机状态变化回调;event 含 cameraStatus、recordAudioPermissionStatus | function | `(event: {cameraStatus, recordAudioPermissionStatus}) => void` | no | iOS/Android | yes | +| onMountError | 相机异常回调;error.message 为错误信息 | function | `(error: {message}) => void` | no | iOS/Android | yes | +| onPictureTaken | 拍照完成回调,无参数 | function | `() => void` | no | iOS/Android | yes | +| onRecordingStart | 录像开始回调;event.nativeEvent 含 uri、videoOrientation、deviceOrientation | function | `(event: {nativeEvent: {uri, videoOrientation, deviceOrientation}}) => void` | no | iOS/Android | yes | +| onRecordingEnd | 录像结束回调,无参数 | function | `() => void` | no | iOS/Android | yes | +| videoStabilizationMode | 视频防抖模式 | string | | no | iOS | no | +| barCodeTypes | 条码识别类型数组 | BarCodeType[] | | no | iOS/Android | no | +| detectedImageInEvent | 扫码回调携带原始图像 | boolean | | no | iOS/Android | no | +| rectOfInterest | 限定条码识别区域 | RectOfInterest | | no | iOS/Android | no | +| googleVisionBarcodeType | 条码类型(Google Vision) | enum | | no | Android | no | +| googleVisionBarcodeMode | 条码识别模式(Google Vision) | enum | | no | Android | no | +| onGoogleVisionBarcodesDetected | Google 条码识别回调 | function | `(event: {type, barcodes, target}) => void` | no | Android | no | +| onFaceDetectionError | 人脸检测错误回调 | function | `(response: {isOperational}) => void` | no | iOS/Android | no | +| faceDetectionMode | 人脸检测模式 | string | | no | iOS/Android | no | +| faceDetectionLandmarks | 人脸特征点检测 | string | | no | iOS/Android | no | +| faceDetectionClassifications | 人脸分类检测 | string | | no | iOS/Android | no | +| trackingEnabled | 人脸跟踪 | boolean | | no | iOS/Android | no | +| onTextRecognized | 文字识别回调 | function | `(response: {textBlocks}) => void` | no | iOS/Android | no | +| onTap | 单击预览回调;origin 为触点坐标 {x, y} | function | `(origin: Point) => void` | no | iOS | no | +| onAudioInterrupted | 音频会话中断回调,无参数 | function | `() => void` | no | iOS | no | +| onAudioConnected | 音频会话连接回调,无参数 | function | `() => void` | no | iOS | no | +| onSubjectAreaChanged | 主体区域变化回调 | function | `(event: {nativeEvent: {prevPoint: {x, y}}}) => void` | no | iOS | no | +| useCamera2Api | 使用 Android Camera2 API | boolean | | no | Android | no | +| playSoundOnCapture | 拍照快门音 | boolean | | no | Android | no | +| permissionDialogTitle | 权限弹窗标题(已废弃) | string | | no | Android | no | +| permissionDialogMessage | 权限弹窗文案(已废弃) | string | | no | Android | no | +| androidCameraPermissionOptions | 相机权限弹窗配置 | object | | no | Android | no | +| androidRecordAudioPermissionOptions | 录音权限弹窗配置 | object | | no | Android | no | +| cameraViewDimensions | 扫码区域尺寸(Android) | object | | no | Android | no | +| keepAudioSession | 卸载时不释放音频会话 | boolean | | no | iOS | no | + +### camera + +| Name | Description | Type | 函数签名 | Required | Platform | HarmonyOS Support | +| ---- | ----------- | ---- | -------- | -------- | --------|-------------------| +| takePictureAsync | 照相;options 可选:quality(0~1)、imageType(jpeg/png)、path 等;resolve 出 TakePictureResponse(width、height、path、pictureOrientation、deviceOrientation) | function | `(options?: TakePictureOptions) => Promise` | no | iOS/Android | yes | +| recordAsync | 开始录像;options 可选:quality、maxDuration、maxFileSize、mute、path 等;resolve 出 RecordResponse(uri、videoOrientation、deviceOrientation、isRecordingInterrupted) | function | `(options?: RecordOptions) => Promise` | no | iOS/Android | yes | +| stopRecording | 停止录像,无参数,无返回值;harmony 停录后有转码延迟,待 recordAsync resolve | function | `() => void` | no | iOS/Android | yes | +| pauseRecording | 暂停录像,无参数,无返回值;仅录像中有效 | function | `() => void` | no | iOS/Android | yes | +| resumeRecording | 恢复录像,无参数,无返回值;仅暂停后有效 | function | `() => void` | no | iOS/Android | yes | +| pausePreview | 暂停预览(画面冻结),无参数,无返回值 | function | `() => void` | no | iOS/Android | yes | +| resumePreview | 恢复预览,无参数,无返回值 | function | `() => void` | no | iOS/Android | yes | +| getCameraIdsAsync | 获取可用摄像头列表;resolve 出 CameraDeviceInfo[],成员:{ id: string(设备 id,可设给 cameraId)、type: number(摄像头类型)、deviceType?: string } | function | `() => Promise` | no | iOS/Android | yes | +| getAvailablePictureSizes | 获取可用图片分辨率;resolve 出 string[],如 "1920x1080" | function | `() => Promise` | no | iOS/Android | yes | +| getSupportedPreviewFpsRange | 获取支持的帧率范围;resolve 出 string[],每项格式 "min,max" | function | `() => Promise` | no | iOS/Android | yes | +| checkIfVideoIsValid | 校验录像文件是否为有效视频;path 为视频文件路径;返回 true=有效、false=文件不存在或非视频 | function | `(path: string) => Promise` | no | iOS/Android | yes | +| hasTorch | 查询设备是否支持闪光灯;返回 true/false | function | `() => Promise` | no | iOS/Android | yes | +| refreshAuthorizationStatus | 重新请求相机/麦克风权限;resolve 后可读取最新授权状态 | function | `() => Promise` | no | iOS/Android | yes | +| getSupportedRatiosAsync | 获取支持的预览宽高比;resolve 出 string[],如 "4:3"、"16:9" | function | `() => Promise` | no | Android | yes | + +## 遗留问题 + +- [ ] onFacesDetected 人脸检测暂不支持 [issue#8](https://github.com/react-native-oh-library/react-native-camera/issues/8) + + +## 其他 + +- notAuthorizedView、pendingAuthorizationView、ratio 是 Android 独有属性;harmony 已提供授权时的系统弹窗。 +- playSoundOnRecord 源于 Android 语义(MediaActionSound),OHOS 相机无快门音开关对等 API,当前为 no-op,属性可正常传入不影响其他功能。 +- checkIfVideoIsValid 失败语义平台差异:harmony 对"文件不存在/元数据解析失败"返回 false;Android 上游对加载失败保守返回 true。 + +## 目录结构 + +```` +/react-native-camera # 项目根目录 +├── harmony # 鸿蒙适配代码 +│ └── reactNativeCamera.har # har包 +│ └── reactNativeCamera # 鸿蒙适配核心代码 +│ └─ index.ets # 鸿蒙适配代码入口 +│ └─ src/main/ets # ArkTS 实现层 +│ └─ src/main/cpp # C++ 层(NativeCameraPackage/Props/RNCCameraModule 等) +├── src # RN 代码 +│ └─ index.js # 入口文件 +│ └─ RNCamera.js / FaceDetector.js # 组件与能力封装 +│ └─ NativeCamera.ts / NativeCameraModule.ts / NativeFaceDetector.ts # codegen spec +├── types # TS 类型定义 +├── README.md # 中文安装使用方法 +├── README_en.md # 英文安装使用方法 +```` + +## 贡献代码 + +使用过程中发现任何问题都可以提交 [Issue](https://github.com/react-native-oh-library/react-native-camera/issues),当然,也非常欢迎提交 [PR](https://github.com/react-native-oh-library/react-native-camera/pulls) 。 + +## 开源协议 + +本项目基于 [MIT License](https://github.com/react-native-camera/react-native-camera/blob/master/LICENSE) ,请自由地享受和参与开源。 diff --git a/README_en.md b/README_en.md new file mode 100644 index 000000000..179d3d71b --- /dev/null +++ b/README_en.md @@ -0,0 +1,398 @@ +> Document Template: v0.4.2 + +

+

react-native-camera

+

+ +This project is based on [react-native-camera](https://github.com/react-native-camera/react-native-camera/tree/v4.2.1). + +Please check the matching version information on the Releases page: [@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases). For older versions not published to npm, please refer to the [Installation Guide](./tgz-usage.md) to install tgz packages. + +| Name | Version(Npm Address) | Release Information | Supported RN Version | Supported Autolink | Compile API Version | Community Baseline Version | Source code address | +| - | - | - | - | - | - | - | - | +| @react-native-ohos/react-native-camera | [~4.0.0 (In development)](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.84.* | Yes | API12+ | v4.2.1 | [sig](https://github.com/react-native-oh-library/react-native-camera/tree/sig) | +| @react-native-ohos/react-native-camera | [~3.42.0](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.82.* | Yes | API12+ | v3.40.0 | [br_rnoh0.82](https://github.com/react-native-oh-library/react-native-camera/tree/br_rnoh0.82) | +| @react-native-ohos/react-native-camera | [~3.41.1](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.77.* | No | API12+ | v3.40.0 | [br_rnoh0.77](https://github.com/react-native-oh-library/react-native-camera/tree/br_rnoh0.77) | +| @react-native-ohos/react-native-camera | [~3.40.1](https://www.npmjs.com/package/@react-native-ohos/react-native-camera) | [Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.72.* | No | API12+ | v3.40.0 | [br_rnoh0.72](https://github.com/react-native-oh-library/react-native-camera/tree/br_rnoh0.72) | + +## Introduction + +react-native-camera is a camera component for React Native, which also reads barcodes.
+This repository hosts `@react-native-ohos/react-native-camera`. The current version (4.0.0-beta.1) adapts to the RN 0.84 framework and upgrades the community baseline to react-native-camera v4.2.1: the JS layer integrates via codegen specs (NativeCamera / NativeCameraModule / NativeFaceDetector), and the HarmonyOS side implements preview, photo capture, video recording and barcode scanning based on Camera Kit / AVRecorder. + +## Installation + +Go to the project directory and execute the following instruction: + +**npm** + +```bash +npm install @react-native-ohos/react-native-camera +``` + +**yarn** + +```bash +yarn add @react-native-ohos/react-native-camera +``` + +## Link + +| | Supported Autolink | RN framework version | +| - | - | - | +| ~4.0.0 | Yes | 0.84 | + +Projects using AutoLink should configure according to this document. Autolink framework guide: https://gitcode.com/openharmony-sig/ohos_react_native/blob/master/docs/en/Autolinking.md + +If the version you use supports Autolink and your project has integrated Autolink, you can skip the ManualLink configuration. +
+ ManualLink: guidance for manually configuring native dependencies + +First, open the `harmony` directory of the project with DevEco Studio. + +### 1. Overrides RN SDK + +To keep the project on a single RN SDK version, add an `overrides` field in the project root `oh-package.json5`, pointing to the RN SDK version required by the project. + +```json +{ + "overrides": { + "@rnoh/react-native-openharmony": "file:./react_native_openharmony.har" + } +} +``` + +### 2. Introducing Native Code + +Method 1: via har package (recommended) + +> [!TIP] The har package is located in the `harmony` folder of the library installation path. + +Open `entry/oh-package.json5` and add the following dependency: + +```json +"dependencies": { + "@react-native-ohos/react-native-camera": "file:../../node_modules/@react-native-ohos/react-native-camera/harmony/reactNativeCamera.har" + } +``` + +Click the `sync` button in the top-right corner, or run: + +```bash +cd entry +ohpm install +``` + +Method 2: direct source linking + +> [!TIP] For direct source linking, please refer to [link-source-code](https://gitcode.com/CPF-RN/usage-docs/blob/master/en/link-source-code.md) + +### 3. Configuring CMakeLists and Introducing NativeCameraPackage + +Open `entry/src/main/cpp/CMakeLists.txt` and add: + +```diff ++ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") + +# RNOH_BEGIN: manual_package_linking_1 ++ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-camera/src/main/cpp" ./reactNativeCamera) +# RNOH_END: manual_package_linking_1 + +# RNOH_BEGIN: manual_package_linking_2 ++ target_link_libraries(rnoh_app PUBLIC rnoh_native_camera) +# RNOH_END: manual_package_linking_2 +``` + +Open `entry/src/main/cpp/PackageProvider.cpp` and add: + +```diff +#include "RNOH/PackageProvider.h" ++ #include "NativeCameraPackage.h" + +using namespace rnoh; + +std::vector> PackageProvider::getPackages(Package::Context ctx) { + return { ++ std::make_shared(ctx) + }; +} +``` + +### 4. Introducing ReactNativeCameraPackage on the ArkTS side + +Open `entry/src/main/ets/RNPackagesFactory.ts` and add: + +```diff + ... ++ import ReactNativeCameraPackage from '@react-native-ohos/react-native-camera'; + +export function createRNPackages(ctx: RNPackageContext): RNPackage[] { + return [ ++ new ReactNativeCameraPackage(ctx) + ]; +} +``` + +> [!TIP] The ArkTS component (ReactCameraView) is provided automatically by the component builder registry inside `ReactNativeCameraPackage` (`createWrappedCustomRNComponentBuilderByComponentNameMap`); the host does not need extra configuration in `buildCustomRNComponent` or `arkTsComponentNames`. +
+ +### Running + +Click the `sync` button in the top-right corner, or run: + +```bash +cd entry +ohpm install +``` + +Then build and run. + +## Constraints + +### Compatibility + +To use this library, a correct React-Native and RNOH version is required, together with matching DevEco Studio and device ROM. + +Verified on: + +1. RNOH: 0.84.2; SDK: HarmonyOS 6.0.1 Release SDK; IDE: DevEco Studio 6.0.1 Release; ROM: 6.0.0.120 SP7; + +### Compilation and Runtime API Requirements + +> [!TIP] All versions of this library are version-isolated and support compiling with `API12+` projects and running on `API12+` ROMs. + +> [!TIP] The following features depend on specific API versions; compiling with projects or running on ROMs below the specified API version may cause partial feature limitation. +1. The `whiteBalance` property requires a project compiled with `API20+` and a ROM supporting `API20+` to take effect. + +### Permission Requirements + +#### Add permissions in `module.json5` under the `entry` directory + +Open `entry/src/main/module.json5` and add: + +```diff +... +"requestPermissions": [ ++ { ++ "name": "ohos.permission.CAMERA", ++ "reason": "$string:camera_reason", ++ "usedScene": { ++ "abilities": [ ++ "EntryAbility" ++ ], ++ "when":"inuse" ++ } ++ }, ++ { ++ "name": "ohos.permission.MICROPHONE", ++ "reason": "$string:microphone_reason", ++ "usedScene": { ++ "abilities": [ ++ "EntryAbility" ++ ], ++ "when":"inuse" ++ } ++ }, +] +``` + +#### Add the permission rationale strings under the `entry` directory + +Open `entry/src/main/resources/base/element/string.json` and add: + +```diff +... +{ + "string": [ ++ { ++ "name": "camera_reason", ++ "value": "Use the camera" ++ }, ++ { ++ "name": "microphone_reason", ++ "value": "Use the microphone" ++ }, + ] +} +``` + +## Example + +The following code shows the basic usage of this library: + +> [!WARNING] The import name stays unchanged. + +```js +import * as React from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { RNCamera } from "@react-native-ohos/react-native-camera"; + +export function CameraExample() { + const cameraRef = React.useRef(null); + + const takePicture = () => { + const camera = cameraRef.current; + if (!camera) { + return; + } + camera + .takePictureAsync({ quality: 0.5 }) + .then((photoResult: any) => { + console.log('takepicture result:' + (photoResult?.path ?? photoResult?.uri)); + }) + .catch((error: unknown) => { + console.warn('takepicture failed:', error); + }); + }; + + return ( + + + + Take Photo + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1 }, + cameraPreview: { width: '100%', aspectRatio: 56 / 100 }, + actionBtn: { + margin: 10, + padding: 10, + backgroundColor: '#1E90FF', + borderRadius: 8, + alignItems: 'center', + }, + actionBtnText: { color: '#fff', fontSize: 16 }, +}); +``` + +## Properties + +> [!TIP] The "Platform" column indicates the platforms supported by the property in the original third-party library. + +> [!TIP] In the "HarmonyOS Support" column, yes means the property is supported on HarmonyOS; no means not supported; partially means partially supported. Usage is consistent across platforms, benchmarked against iOS or Android behavior. + +### RNCamera + +| Name | Description | Type | Function Signature | Required | Platform | HarmonyOS Support | +| ---- | ----------- | ---- | ------------------ | -------- |----------|-------------------| +| children | Set child components | View | | no | iOS/Android | yes | +| type | Select camera | string | | no | iOS/Android | yes | +| cameraId | Precisely specify the camera device (the id returned by getCameraIdsAsync) | string | | no | iOS/Android | yes | +| flashMode | Set flash mode | string | | no | iOS/Android | yes | +| exposure | Set exposure | number | | no | iOS/Android | yes | +| autoFocus | Set auto focus | string | | no | iOS/Android | yes | +| whiteBalance | White balance | string | | no | iOS/Android | yes | +| captureAudio | Enable audio recording | boolean | | no | iOS/Android | yes | +| zoom | Set zoom ratio | number | | no | iOS/Android | yes | +| focusDepth | Set focus value in non-auto-focus mode | number | | no | iOS/Android | yes | +| maxZoom | Set max zoom ratio | number | | no | iOS/Android | yes | +| pictureSize | Set default picture resolution | string | | no | iOS/Android | yes | +| playSoundOnRecord | Play system sound at recording start/stop | boolean | | no | Android | no | +| onCameraReady | Camera ready callback, no arguments | function | `() => void` | no | iOS/Android | yes | +| onBarCodeRead | Barcode scanning callback; event contains data (barcode content), type (barcode format), bounds (position) | function | `(event: BarCodeReadEvent) => void` | no | iOS/Android | yes | +| onFacesDetected | Face detection callback; response.faces is the face array (not yet supported on harmony) | function | `(response: {faces: Face[]}) => void` | no | iOS/Android | no | +| onDoubleTap | Double tap on preview callback; origin is the touch point {x, y} (component-relative) | function | `(origin: Point) => void` | no | iOS/Android | yes | +| notAuthorizedView | UI shown when not authorized | View | | no | Android | no | +| pendingAuthorizationView | UI shown while authorizing | View | | no | Android | no | +| ratio | Camera ratio | string | | no | Android | no | +| defaultVideoQuality | Default video quality | string | | no | iOS/Android | yes | +| autoFocusPointOfInterest | Set auto focus point (x/y) | Point | | no | iOS/Android | no | +| useNativeZoom | Enable native pinch-to-zoom gesture | boolean | | no | iOS/Android | yes | +| onStatusChange | Camera status change callback; event contains cameraStatus, recordAudioPermissionStatus | function | `(event: {cameraStatus, recordAudioPermissionStatus}) => void` | no | iOS/Android | yes | +| onMountError | Camera error callback; error.message is the error message | function | `(error: {message}) => void` | no | iOS/Android | yes | +| onPictureTaken | Picture taken callback, no arguments | function | `() => void` | no | iOS/Android | yes | +| onRecordingStart | Recording start callback; event.nativeEvent contains uri, videoOrientation, deviceOrientation | function | `(event: {nativeEvent: {uri, videoOrientation, deviceOrientation}}) => void` | no | iOS/Android | yes | +| onRecordingEnd | Recording end callback, no arguments | function | `() => void` | no | iOS/Android | yes | +| videoStabilizationMode | Video stabilization mode | string | | no | iOS | no | +| barCodeTypes | Barcode type array | BarCodeType[] | | no | iOS/Android | no | +| detectedImageInEvent | Include raw image in barcode callback | boolean | | no | iOS/Android | no | +| rectOfInterest | Limit barcode recognition area | RectOfInterest | | no | iOS/Android | no | +| googleVisionBarcodeType | Barcode type (Google Vision) | enum | | no | Android | no | +| googleVisionBarcodeMode | Barcode recognition mode (Google Vision) | enum | | no | Android | no | +| onGoogleVisionBarcodesDetected | Google Vision barcode callback | function | `(event: {type, barcodes, target}) => void` | no | Android | no | +| onFaceDetectionError | Face detection error callback | function | `(response: {isOperational}) => void` | no | iOS/Android | no | +| faceDetectionMode | Face detection mode | string | | no | iOS/Android | no | +| faceDetectionLandmarks | Face landmarks detection | string | | no | iOS/Android | no | +| faceDetectionClassifications | Face classification detection | string | | no | iOS/Android | no | +| trackingEnabled | Face tracking | boolean | | no | iOS/Android | no | +| onTextRecognized | Text recognition callback | function | `(response: {textBlocks}) => void` | no | iOS/Android | no | +| onTap | Single tap on preview callback; origin is the touch point {x, y} | function | `(origin: Point) => void` | no | iOS | no | +| onAudioInterrupted | Audio session interruption callback, no arguments | function | `() => void` | no | iOS | no | +| onAudioConnected | Audio session connected callback, no arguments | function | `() => void` | no | iOS | no | +| onSubjectAreaChanged | Subject area changed callback | function | `(event: {nativeEvent: {prevPoint: {x, y}}}) => void` | no | iOS | no | +| useCamera2Api | Use Android Camera2 API | boolean | | no | Android | no | +| playSoundOnCapture | Shutter sound on capture | boolean | | no | Android | no | +| permissionDialogTitle | Permission dialog title (deprecated) | string | | no | Android | no | +| permissionDialogMessage | Permission dialog message (deprecated) | string | | no | Android | no | +| androidCameraPermissionOptions | Camera permission dialog config | object | | no | Android | no | +| androidRecordAudioPermissionOptions | Record-audio permission dialog config | object | | no | Android | no | +| cameraViewDimensions | Scan area dimensions (Android) | object | | no | Android | no | +| keepAudioSession | Keep audio session on unmount | boolean | | no | iOS | no | + +### camera + +| Name | Description | Type | Function Signature | Required | Platform | HarmonyOS Support | +| ---- | ----------- | ---- | ------------------ | -------- | --------|-------------------| +| takePictureAsync | Take a photo; optional options: quality (0~1), imageType (jpeg/png), path, etc.; resolves with TakePictureResponse (width, height, path, pictureOrientation, deviceOrientation) | function | `(options?: TakePictureOptions) => Promise` | no | iOS/Android | yes | +| recordAsync | Start recording; optional options: quality, maxDuration, maxFileSize, mute, path, etc.; resolves with RecordResponse (uri, videoOrientation, deviceOrientation, isRecordingInterrupted) | function | `(options?: RecordOptions) => Promise` | no | iOS/Android | yes | +| stopRecording | Stop recording, no arguments, no return value; on harmony there is a transcoding delay before recordAsync resolves | function | `() => void` | no | iOS/Android | yes | +| pauseRecording | Pause recording, no arguments, no return value; valid only while recording | function | `() => void` | no | iOS/Android | yes | +| resumeRecording | Resume recording, no arguments, no return value; valid only after pause | function | `() => void` | no | iOS/Android | yes | +| pausePreview | Pause preview (frame freeze), no arguments, no return value | function | `() => void` | no | iOS/Android | yes | +| resumePreview | Resume preview, no arguments, no return value | function | `() => void` | no | iOS/Android | yes | +| getCameraIdsAsync | Get available cameras; resolves with CameraDeviceInfo[], members: { id: string (device id, settable to cameraId), type: number (camera type), deviceType?: string } | function | `() => Promise` | no | iOS/Android | yes | +| getAvailablePictureSizes | Get available picture sizes; resolves with string[] such as "1920x1080" | function | `() => Promise` | no | iOS/Android | yes | +| getSupportedPreviewFpsRange | Get supported fps ranges; resolves with string[], each item "min,max" | function | `() => Promise` | no | iOS/Android | yes | +| checkIfVideoIsValid | Validate a recorded file; path is the video file path; returns true = valid, false = not found or not a video | function | `(path: string) => Promise` | no | iOS/Android | yes | +| hasTorch | Query flash support; returns true/false | function | `() => Promise` | no | iOS/Android | yes | +| refreshAuthorizationStatus | Re-request camera/microphone permission; after resolve the latest authorization status can be read | function | `() => Promise` | no | iOS/Android | yes | +| getSupportedRatiosAsync | Get supported preview aspect ratios; resolves with string[] such as "4:3", "16:9" | function | `() => Promise` | no | Android | yes | + +## Known Issues + +- [ ] onFacesDetected (face detection) is not yet supported [issue#8](https://github.com/react-native-oh-library/react-native-camera/issues/8) + + +## Others + +- notAuthorizedView, pendingAuthorizationView and ratio are Android-only properties; HarmonyOS provides the system permission dialog when requesting authorization. +- playSoundOnRecord originates from Android semantics (MediaActionSound); the OHOS camera has no shutter-sound equivalent API, so it is currently a no-op. The property can be passed normally without affecting other features. +- checkIfVideoIsValid failure semantics differ by platform: HarmonyOS returns false for "file not found / metadata parsing failure"; the Android upstream conservatively returns true on load failure. + +## Directory Structure + +```` +/react-native-camera # Project root +├── harmony # HarmonyOS adaptation code +│ └── reactNativeCamera.har # har package +│ └── reactNativeCamera # Core HarmonyOS adaptation code +│ └─ index.ets # Entry of the HarmonyOS adaptation code +│ └─ src/main/ets # ArkTS implementation layer +│ └─ src/main/cpp # C++ layer (NativeCameraPackage/Props/RNCCameraModule, etc.) +├── src # RN code +│ └─ index.js # Entry file +│ └─ RNCamera.js / FaceDetector.js # Component and capability wrappers +│ └─ NativeCamera.ts / NativeCameraModule.ts / NativeFaceDetector.ts # codegen specs +├── types # TS type definitions +├── README.md # Chinese installation and usage guide +├── README_en.md # English installation and usage guide +```` + +## Contributing + +If you find any problem during usage, feel free to submit an [Issue](https://github.com/react-native-oh-library/react-native-camera/issues); PRs are also welcome ([Pull Requests](https://github.com/react-native-oh-library/react-native-camera/pulls)). + +## License + +This project is based on the [MIT License](https://github.com/react-native-camera/react-native-camera/blob/master/LICENSE), please feel free to enjoy and participate in open source. diff --git a/harmony/reactNativeCamera.har b/harmony/reactNativeCamera.har index 83fa54826..864753264 100644 Binary files a/harmony/reactNativeCamera.har and b/harmony/reactNativeCamera.har differ diff --git a/harmony/reactNativeCamera/index.ets b/harmony/reactNativeCamera/index.ets index 0ddc9136f..45f8e0d79 100644 --- a/harmony/reactNativeCamera/index.ets +++ b/harmony/reactNativeCamera/index.ets @@ -1,26 +1,18 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + export * from "./ts" export * from "./src/main/ets/ReactCameraView" export { ReactNativeCameraPackage as default } from "./src/main/ets/ReactNativeCameraPackage" diff --git a/harmony/reactNativeCamera/oh-package.json5 b/harmony/reactNativeCamera/oh-package.json5 index 59b8a7aa8..fadba5daf 100644 --- a/harmony/reactNativeCamera/oh-package.json5 +++ b/harmony/reactNativeCamera/oh-package.json5 @@ -6,9 +6,9 @@ }, "author": "", "name": "@react-native-ohos/react-native-camera", - "description": "Please describe the basic information.", + "description": "React Native camera component for OpenHarmony. Supports camera preview, photo capture, video recording, barcode scanning, with flash/focus/white-balance/zoom control.", "main": "index.ets", - "version": "3.42.1-rc.1", + "version": "4.0.0-beta.1", "dependencies": { "@rnoh/react-native-openharmony": "file:../../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har", "@ohos/mp4parser": "^2.0.6" diff --git a/harmony/reactNativeCamera/src/main/cpp/ComponentDescriptors.h b/harmony/reactNativeCamera/src/main/cpp/ComponentDescriptors.h index 9496f8e0b..7c10d3f9f 100644 --- a/harmony/reactNativeCamera/src/main/cpp/ComponentDescriptors.h +++ b/harmony/reactNativeCamera/src/main/cpp/ComponentDescriptors.h @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * diff --git a/harmony/reactNativeCamera/src/main/cpp/EventEmitters.cpp b/harmony/reactNativeCamera/src/main/cpp/EventEmitters.cpp index d96f1d421..eea0294d2 100644 --- a/harmony/reactNativeCamera/src/main/cpp/EventEmitters.cpp +++ b/harmony/reactNativeCamera/src/main/cpp/EventEmitters.cpp @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * diff --git a/harmony/reactNativeCamera/src/main/cpp/EventEmitters.h b/harmony/reactNativeCamera/src/main/cpp/EventEmitters.h index 94713dc78..e27c80e24 100644 --- a/harmony/reactNativeCamera/src/main/cpp/EventEmitters.h +++ b/harmony/reactNativeCamera/src/main/cpp/EventEmitters.h @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * diff --git a/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.cpp b/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.cpp index 42f0fdb07..655b11a7f 100644 --- a/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.cpp +++ b/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.cpp @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by "react-native codegen-harmony" * diff --git a/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.h b/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.h index fbe8f5797..2dcafa063 100644 --- a/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.h +++ b/harmony/reactNativeCamera/src/main/cpp/FaceNativeTurboModule.h @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by "react-native codegen-harmony" * diff --git a/harmony/reactNativeCamera/src/main/cpp/NativeCameraComponentDescriptor.h b/harmony/reactNativeCamera/src/main/cpp/NativeCameraComponentDescriptor.h index 6ada4a791..0af56bd11 100644 --- a/harmony/reactNativeCamera/src/main/cpp/NativeCameraComponentDescriptor.h +++ b/harmony/reactNativeCamera/src/main/cpp/NativeCameraComponentDescriptor.h @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by "react-native codegen-harmony" * diff --git a/harmony/reactNativeCamera/src/main/cpp/NativeCameraEventEmitRequestHandler.h b/harmony/reactNativeCamera/src/main/cpp/NativeCameraEventEmitRequestHandler.h index 90318daff..35977b9e4 100644 --- a/harmony/reactNativeCamera/src/main/cpp/NativeCameraEventEmitRequestHandler.h +++ b/harmony/reactNativeCamera/src/main/cpp/NativeCameraEventEmitRequestHandler.h @@ -1,26 +1,18 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2024 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + #ifndef HARMONY_NATIVECAMERAEVENTEMITREQUESTHANDLER_H #define HARMONY_NATIVECAMERAEVENTEMITREQUESTHANDLER_H diff --git a/harmony/reactNativeCamera/src/main/cpp/NativeCameraJSIBinder.h b/harmony/reactNativeCamera/src/main/cpp/NativeCameraJSIBinder.h index dcebb428c..914498ec3 100644 --- a/harmony/reactNativeCamera/src/main/cpp/NativeCameraJSIBinder.h +++ b/harmony/reactNativeCamera/src/main/cpp/NativeCameraJSIBinder.h @@ -1,26 +1,18 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + #pragma once /** @@ -51,6 +43,7 @@ class NativeCameraJSIBinder : public ViewComponentJSIBinder { object.setProperty(rt, "testID", true); object.setProperty(rt, "autoFocus", true); object.setProperty(rt, "autoFocusPointOfInterest", "Object"); + object.setProperty(rt, "cameraId", ""); object.setProperty(rt, "pictureSize", ""); object.setProperty(rt, "type", ""); object.setProperty(rt, "flashMode", true); @@ -71,6 +64,7 @@ class NativeCameraJSIBinder : public ViewComponentJSIBinder { object.setProperty(rt, "trackingEnabled", true); object.setProperty(rt, "ratio", true); object.setProperty(rt, "playSoundOnCapture", true); + object.setProperty(rt, "playSoundOnRecord", true); object.setProperty(rt, "videoStabilizationMode", ""); object.setProperty(rt, "defaultVideoQuality", true); object.setProperty(rt, "keepAudioSession", true); diff --git a/harmony/reactNativeCamera/src/main/cpp/NativeCameraPackage.h b/harmony/reactNativeCamera/src/main/cpp/NativeCameraPackage.h index b75c65aa9..6c1cd470d 100644 --- a/harmony/reactNativeCamera/src/main/cpp/NativeCameraPackage.h +++ b/harmony/reactNativeCamera/src/main/cpp/NativeCameraPackage.h @@ -1,26 +1,18 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2024 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + #pragma once #include "ComponentDescriptors.h" #include "FaceNativeTurboModule.h" diff --git a/harmony/reactNativeCamera/src/main/cpp/Props.cpp b/harmony/reactNativeCamera/src/main/cpp/Props.cpp index fec4681e1..f3287023e 100644 --- a/harmony/reactNativeCamera/src/main/cpp/Props.cpp +++ b/harmony/reactNativeCamera/src/main/cpp/Props.cpp @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * @@ -56,6 +48,7 @@ NativeCameraProps::NativeCameraProps( autoFocus(convertRawProp(context, rawProps, "autoFocus", sourceProps.autoFocus, {AutoFocusMode::ON})), autoFocusPointOfInterest(convertRawProp(context, rawProps, "autoFocusPointOfInterest", sourceProps.autoFocusPointOfInterest, {})), pictureSize(convertRawProp(context, rawProps, "pictureSize", sourceProps.pictureSize, {""})), + cameraId(convertRawProp(context, rawProps, "cameraId", sourceProps.cameraId, {""})), // 4.2.1 新增 type(convertRawProp(context, rawProps, "type", sourceProps.type, {CameraType::BACK})), flashMode(convertRawProp(context, rawProps, "flashMode", sourceProps.flashMode, {FlashMode::AUTO})), exposure(convertRawProp(context, rawProps, "exposure", sourceProps.exposure, {0.0})), @@ -75,6 +68,7 @@ NativeCameraProps::NativeCameraProps( trackingEnabled(convertRawProp(context, rawProps, "trackingEnabled", sourceProps.trackingEnabled, {true})), ratio(convertRawProp(context, rawProps, "ratio", sourceProps.ratio, {true})), playSoundOnCapture(convertRawProp(context, rawProps, "playSoundOnCapture", sourceProps.playSoundOnCapture, {true})), + playSoundOnRecord(convertRawProp(context, rawProps, "playSoundOnRecord", sourceProps.playSoundOnRecord, {false})), // 4.2.1 新增 videoStabilizationMode(convertRawProp(context, rawProps, "videoStabilizationMode", sourceProps.videoStabilizationMode, {VideoStabilizationMode::AUTO})), defaultVideoQuality(convertRawProp(context, rawProps, "defaultVideoQuality", sourceProps.defaultVideoQuality, {VideoQuality::ONE_ZERO_P})), whiteBalance(convertRawProp(context, rawProps, "whiteBalance", sourceProps.whiteBalance, {WhiteBalance::AUTO})), diff --git a/harmony/reactNativeCamera/src/main/cpp/Props.h b/harmony/reactNativeCamera/src/main/cpp/Props.h index c5b1704a2..47ce849f2 100644 --- a/harmony/reactNativeCamera/src/main/cpp/Props.h +++ b/harmony/reactNativeCamera/src/main/cpp/Props.h @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * @@ -329,6 +321,7 @@ class NativeCameraProps final : public ViewProps { AutoFocusMode autoFocus{AutoFocusMode::ON}; FocusPoint autoFocusPointOfInterest{}; std::string pictureSize{}; + std::string cameraId{}; // 4.2.1 新增:精确指定摄像头设备 CameraType type{CameraType::BACK}; FlashMode flashMode{FlashMode::AUTO}; Float exposure{0.0}; @@ -347,6 +340,7 @@ class NativeCameraProps final : public ViewProps { bool trackingEnabled{false}; std::string ratio{}; bool playSoundOnCapture{false}; + bool playSoundOnRecord{false}; // 4.2.1 新增:Android 语义,OHOS 无对等 no-op VideoStabilizationMode videoStabilizationMode{VideoStabilizationMode::AUTO}; VideoQuality defaultVideoQuality{VideoQuality::ONE_ZERO_P}; bool keepAudioSession{false}; diff --git a/harmony/reactNativeCamera/src/main/cpp/RNCCameraModule.cpp b/harmony/reactNativeCamera/src/main/cpp/RNCCameraModule.cpp index 70db454ed..9ce7979a5 100644 --- a/harmony/reactNativeCamera/src/main/cpp/RNCCameraModule.cpp +++ b/harmony/reactNativeCamera/src/main/cpp/RNCCameraModule.cpp @@ -13,6 +13,10 @@ RNCCameraModule::RNCCameraModule(const ArkTSTurboModule::Context ctx, const std: ARK_ASYNC_METHOD_METADATA(getAuidPermission, 0), ARK_ASYNC_METHOD_METADATA(getCameraIds, 0), ARK_ASYNC_METHOD_METADATA(getAvailablePictureSizes, 0), + ARK_ASYNC_METHOD_METADATA(hasTorch, 0), + ARK_ASYNC_METHOD_METADATA(checkIfVideoIsValid, 1), + ARK_ASYNC_METHOD_METADATA(getSupportedPreviewFpsRange, 1), + ARK_ASYNC_METHOD_METADATA(getSupportedRatios, 1), }; } diff --git a/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.cpp b/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.cpp index 42b0528c7..7bba76f5a 100644 --- a/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.cpp +++ b/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.cpp @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * diff --git a/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.h b/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.h index c9e973350..5d625adf9 100644 --- a/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.h +++ b/harmony/reactNativeCamera/src/main/cpp/ShadowNodes.h @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * diff --git a/harmony/reactNativeCamera/src/main/cpp/States.cpp b/harmony/reactNativeCamera/src/main/cpp/States.cpp index 5aa651cb9..ae23844c1 100644 --- a/harmony/reactNativeCamera/src/main/cpp/States.cpp +++ b/harmony/reactNativeCamera/src/main/cpp/States.cpp @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * diff --git a/harmony/reactNativeCamera/src/main/cpp/States.h b/harmony/reactNativeCamera/src/main/cpp/States.h index 7f259f9ab..99b458d35 100644 --- a/harmony/reactNativeCamera/src/main/cpp/States.h +++ b/harmony/reactNativeCamera/src/main/cpp/States.h @@ -1,27 +1,19 @@ -/** - * MIT License - * - * Copyright (C) 2024 Huawei Device Co., Ltd. +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * diff --git a/harmony/reactNativeCamera/src/main/ets/FaceDectorPackage.ets b/harmony/reactNativeCamera/src/main/ets/FaceDectorPackage.ets index a9a244934..2e749241a 100644 --- a/harmony/reactNativeCamera/src/main/ets/FaceDectorPackage.ets +++ b/harmony/reactNativeCamera/src/main/ets/FaceDectorPackage.ets @@ -1,27 +1,19 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { TurboModulesFactory } from '@rnoh/react-native-openharmony/ts'; import { RNOHPackage } from '@rnoh/react-native-openharmony'; import type { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; diff --git a/harmony/reactNativeCamera/src/main/ets/FaceDectorTurboModule.ts b/harmony/reactNativeCamera/src/main/ets/FaceDectorTurboModule.ts index 88fc17e2f..b0e6edf4c 100644 --- a/harmony/reactNativeCamera/src/main/ets/FaceDectorTurboModule.ts +++ b/harmony/reactNativeCamera/src/main/ets/FaceDectorTurboModule.ts @@ -1,28 +1,20 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import type { TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; import { TurboModule } from '@rnoh/react-native-openharmony/ts'; import Logger from './Logger'; diff --git a/harmony/reactNativeCamera/src/main/ets/Logger.ts b/harmony/reactNativeCamera/src/main/ets/Logger.ts index c1adbab8a..1c6520beb 100644 --- a/harmony/reactNativeCamera/src/main/ets/Logger.ts +++ b/harmony/reactNativeCamera/src/main/ets/Logger.ts @@ -1,27 +1,19 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import hilog from '@ohos.hilog'; class Logger { diff --git a/harmony/reactNativeCamera/src/main/ets/ReactCameraView.ets b/harmony/reactNativeCamera/src/main/ets/ReactCameraView.ets index adf4aab06..837761ab8 100644 --- a/harmony/reactNativeCamera/src/main/ets/ReactCameraView.ets +++ b/harmony/reactNativeCamera/src/main/ets/ReactCameraView.ets @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import camera from '@ohos.multimedia.camera'; import { Descriptor, RNComponentContext, RNViewBase } from '@rnoh/react-native-openharmony'; import Logger from './utils/Logger'; @@ -118,6 +128,7 @@ export struct ReactCameraView { @State isRecording: boolean = false @State videoStabilizationMode: string = "" @State maxZoom: number = 0 + private prevCameraId: string = '' aboutToAppear() { this.localDisplay = display.getDefaultDisplaySync(); @@ -205,11 +216,22 @@ export struct ReactCameraView { this.exposure = descriptor.props.exposure; } - if (descriptor.props.useNativeZoom) { + // 修复 truthy 检查: false 时也要更新状态, 否则 Switch 关闭后捏合变焦仍生效 + if (descriptor.props.useNativeZoom != null) { Logger.debug(`RNOH in NativeCamera descriptor useNativeZoom, ${JSON.stringify(descriptor.props.useNativeZoom)}`); this.useNativeZoom = descriptor.props.useNativeZoom; } + // 接线 autoFocusPointOfInterest: prop 为 0~1 归一化坐标, 换算组件像素后交 CameraSession.focus → setFocusPoint + // cameraSession 在 surface 就绪前是 {} as CameraSession 空对象, 需检查 focus 方法存在 + if (descriptor.props.autoFocusPointOfInterest != null && this.cameraSession?.focus) { + const p = descriptor.props.autoFocusPointOfInterest; + Logger.debug(`RNOH in NativeCamera autoFocusPointOfInterest, ${JSON.stringify(p)}`); + this.cameraSession.focus(this.focusMode, { + x: p.x * this.localDisplayWidth, + y: p.y * this.localDisplayHeight + }); + } if (descriptor.props.focusDepth) { Logger.debug(`RNOH in NativeCamera descriptor focusDepth, ${JSON.stringify(descriptor.props.focusDepth)}`); @@ -226,6 +248,20 @@ export struct ReactCameraView { private updateProp(descriptor: ReactNativeCameraDescriptor) { Logger.debug(`RNOH in NativeCamera descriptor props, ${JSON.stringify(descriptor.props)}`); this.tempProp = descriptor.props; + if (descriptor.props.cameraId && descriptor.props.cameraId !== '' && + this.prevCameraId !== descriptor.props.cameraId) { + // 4.2.1 新增 cameraId prop:设备切换走 changeCameraPosition 重新 initCamera + this.prevCameraId = descriptor.props.cameraId; + this.cameraSession.changeCameraPosition({ + surfaceId: this.surfaceId, props: this.tempProp, + mediaModel: this.cameraState === cameraState.PHOTO ? camera.SceneMode.NORMAL_PHOTO : + camera.SceneMode.NORMAL_VIDEO + }) + } + if (descriptor.props.playSoundOnRecord != null) { + // 4.2.1 新增 playSoundOnRecord prop:OHOS camera API 无快门音开关对等,行为 no-op(合理差异) + Logger.debug(`RNOH in NativeCamera descriptor playSoundOnRecord, ${JSON.stringify(descriptor.props.playSoundOnRecord)}`); + } this.cameraSession.changeCameraPosition({ surfaceId: this.surfaceId, props: this.tempProp, mediaModel: this.cameraState === cameraState.PHOTO ? camera.SceneMode.NORMAL_PHOTO : camera.SceneMode.NORMAL_VIDEO @@ -261,11 +297,23 @@ export struct ReactCameraView { this.cameraSession.setAudio(descriptor.props.captureAudio) } - if (descriptor.props.useNativeZoom) { + // 修复 truthy 检查: false 时也要更新状态, 否则 Switch 关闭后捏合变焦仍生效 + if (descriptor.props.useNativeZoom != null) { Logger.debug(`RNOH in NativeCamera descriptor useNativeZoom, ${JSON.stringify(descriptor.props.useNativeZoom)}`); this.useNativeZoom = descriptor.props.useNativeZoom; } + // 接线 autoFocusPointOfInterest: prop 为 0~1 归一化坐标, 换算组件像素后交 CameraSession.focus → setFocusPoint + // cameraSession 在 surface 就绪前是 {} as CameraSession 空对象, 需检查 focus 方法存在 + if (descriptor.props.autoFocusPointOfInterest != null && this.cameraSession?.focus) { + const p = descriptor.props.autoFocusPointOfInterest; + Logger.debug(`RNOH in NativeCamera autoFocusPointOfInterest, ${JSON.stringify(p)}`); + this.cameraSession.focus(this.focusMode, { + x: p.x * this.localDisplayWidth, + y: p.y * this.localDisplayHeight + }); + } + if (descriptor.props.maxZoom > 0) { this.maxZoom = descriptor.props.maxZoom } @@ -486,6 +534,7 @@ export struct ReactCameraView { } if (command === 'record') { const options = args[0] as RecordVideoOptions; + Logger.info(TAG, 'CMD-IN record command received, options=' + JSON.stringify(options)); Logger.debug(TAG, `registerCommandCallback: record.${JSON.stringify(options)}}`) if (this.cameraState == cameraState.PHOTO) { this.cameraState = cameraState.VIDEO @@ -512,6 +561,7 @@ export struct ReactCameraView { } if (command === 'stopRecording') { + Logger.info(TAG, 'CMD-IN stopRecording command received'); Logger.debug(TAG, `registerCommandCallback: stopRecording, ${command}`) this.stopRecording(); this.ctx.rnInstance.emitComponentEvent( @@ -685,13 +735,19 @@ export struct ReactCameraView { * 停止录制 */ async stopRecording() { - await this.cameraSession.stopRecording() - this.isRecording = false - //当帧率设置30fps时直接回调,不需要转换,系统默认录制出来是30fps - if (this.cameraSession.fps == 30) { - Logger.debug("stopRecording fps 30") - this.cameraSession.sendOnRecordingFinishedEvent(OrientationUtils.getDefaultDisplay()); - return; + try { + await this.cameraSession.stopRecording() + } catch (error) { + // session 停止失败(reset 状态冲突等)不能阻断完成事件回调 + Logger.error(TAG, 'stopRecording: session stop failed: ' + JSON.stringify(error)); + } finally { + this.isRecording = false + //当帧率设置30fps时直接回调,不需要转换,系统默认录制出来是30fps + if (this.cameraSession.fps == 30) { + Logger.debug("stopRecording fps 30") + this.cameraSession.sendOnRecordingFinishedEvent(OrientationUtils.getDefaultDisplay()); + return; + } } //由于系统Api media.AVRecorder->prepare录制后videoFrameRate设置无效,因此使用FFmpeg处理帧率转换 let inPath = this.cameraSession.videoUri; @@ -815,7 +871,8 @@ export struct ReactCameraView { .height('100%') .justifyContent(FlexAlign.Center) .gesture( - GestureGroup(GestureMode.Parallel, PinchGesture({ fingers: 2 })// 当捏合手势触发时,可以通过回调函数获取缩放比例,从而修改组件的缩放比例 + GestureGroup(GestureMode.Parallel, + PinchGesture({ fingers: 2 })// 当捏合手势触发时,可以通过回调函数获取缩放比例,从而修改组件的缩放比例 .onActionUpdate((event: GestureEvent) => { this.scaleValue = this.cameraSession.photoPreviewScale * event.scale; }) @@ -827,9 +884,30 @@ export struct ReactCameraView { this.cameraSession?.setNativeSmoothZoom(this.scaleValue); } } - })) - // 在组件上绑定三指触发的捏合手势 - + }) + , + // onDoubleTap 链路: 双击手势 → emit onTouch{tapCallback:{isDoubleTap, touchOrigin}} → cpp handler → JS _onTouch 分发 onDoubleTap + TapGesture({ count: 2 }) + .onAction((event: GestureEvent) => { + // GestureEvent 静态类型无坐标, 运行时为 TapGestureEvent, 经 tapLocation 取组件相对坐标(vp) + const tapLocation = (event as TapGestureEvent).tapLocation; + this.ctx.rnInstance.emitComponentEvent( + this.tag, + VisionCameraViewSpec.NAME, + { + type: 'onTouch', + // cpp 侧 NativeCameraEventEmitRequestHandler 从 payload.tapCallback 取值 + tapCallback: { + isDoubleTap: true, + touchOrigin: { + x: tapLocation ? Math.round(tapLocation.x) : 0, + y: tapLocation ? Math.round(tapLocation.y) : 0 + } + } + } + ) + }) + ) ) } .width(this.localDisplayWidth + 'px') diff --git a/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraPackage.ets b/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraPackage.ets index e8b089182..e3e3c310e 100644 --- a/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraPackage.ets +++ b/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraPackage.ets @@ -1,32 +1,25 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { TurboModulesFactory } from '@rnoh/react-native-openharmony/ts'; -import { RNOHPackage } from '@rnoh/react-native-openharmony'; +import { RNOHPackage, ComponentBuilderContext } from '@rnoh/react-native-openharmony'; import type { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; import { ReactNativeCameraTurboModule } from './ReactNativeCameraTurboModule'; import { FaceDectorTurboModule } from './FaceDectorTurboModule'; +import { ReactCameraView } from './ReactCameraView'; class ReactNativeCameraTurboModulesFactory extends TurboModulesFactory { createTurboModule(name: string): TurboModule | null { @@ -44,8 +37,27 @@ class ReactNativeCameraTurboModulesFactory extends TurboModulesFactory { } } +@Builder +function buildRNWebRTCComponent(ctx: ComponentBuilderContext) { + Stack() { + if (ctx.componentName === ReactCameraView.NAME) { + ReactCameraView({ + ctx: ctx.rnComponentContext, + tag: ctx.tag + }) + } + } + .position({ x: 0, y: 0 }) +} + export class ReactNativeCameraPackage extends RNOHPackage { createTurboModulesFactory(ctx: TurboModuleContext): TurboModulesFactory { return new ReactNativeCameraTurboModulesFactory(ctx); } + + createWrappedCustomRNComponentBuilderByComponentNameMap(): Map> { + const map = new Map>() + map.set(ReactCameraView.NAME, wrapBuilder(buildRNWebRTCComponent)) + return map + } } diff --git a/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraTurboModule.ts b/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraTurboModule.ts index 3b1f3e02d..2ccbfb765 100644 --- a/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraTurboModule.ts +++ b/harmony/reactNativeCamera/src/main/ets/ReactNativeCameraTurboModule.ts @@ -1,28 +1,20 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import type { TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; import { TurboModule } from '@rnoh/react-native-openharmony/ts'; import { BusinessError, emitter } from '@kit.BasicServicesKit'; @@ -33,6 +25,8 @@ import Logger from './Logger'; import { SimpleCameraDeviceInfo } from './core/CameraDeviceInfo'; import { camera } from '@kit.CameraKit'; import { AuthType } from './types/AuthType'; +import { media } from '@kit.MediaKit'; +import fs from '@ohos.file.fs'; const TAG: string = '[RNOH]ReactNativeCameraTurboModule' @@ -158,12 +152,26 @@ export class ReactNativeCameraTurboModule extends TurboModule { let temp = new SimpleCameraDeviceInfo(); temp.id = camerasArrayTemp[i].cameraId; temp.type = camerasArrayTemp[i].cameraType; + temp.deviceType = this.getDeviceType(camerasArrayTemp[i].cameraType); data.push(temp) } resolve(data) }); } + private getDeviceType(cameraType: camera.CameraType): string { + switch (cameraType) { + case camera.CameraType.CAMERA_TYPE_ULTRA_WIDE: + return 'AVCaptureDeviceTypeBuiltInUltraWideCamera'; + case camera.CameraType.CAMERA_TYPE_WIDE_ANGLE: + return 'AVCaptureDeviceTypeBuiltInWideAngleCamera'; + case camera.CameraType.CAMERA_TYPE_TELEPHOTO: + return 'AVCaptureDeviceTypeBuiltInTelephotoCamera'; + default: + return 'AVCaptureDeviceTypeBuiltInWideAngleCamera'; + } + } + getAvailablePictureSizes(): Promise { Logger.debug("getAvailablePictureSizes") return new Promise(async (resolve, reject) => { @@ -182,4 +190,118 @@ export class ReactNativeCameraTurboModule extends TurboModule { resolve(data) }); } + + hasTorch(): Promise { + Logger.debug("hasTorch") + return new Promise((resolve, reject) => { + try { + let cameraManager = camera.getCameraManager(this.ctx.getUIContext().getHostContext()); + let isSupported: boolean = cameraManager.isTorchModeSupported(camera.TorchMode.ON); + resolve(isSupported) + } catch (err) { + let businessError = err as BusinessError; + Logger.error(TAG, `hasTorch failed, code: ${businessError.code}, message: ${businessError.message}`); + reject(businessError) + } + }); + } + + checkIfVideoIsValid(path: string): Promise { + Logger.debug("checkIfVideoIsValid") + return new Promise(async (resolve, reject) => { + let fileExists = fs.accessSync(path); + if (!fileExists) { + resolve(false) + return; + } + let avMetadataExtractor: media.AVMetadataExtractor = await media.createAVMetadataExtractor(); + let file = fs.openSync(path, fs.OpenMode.READ_ONLY); + avMetadataExtractor.fdSrc = { + fd: file.fd + } + avMetadataExtractor.fetchMetadata().then((metadata) => { + if (metadata.duration.length > 0 && parseInt(metadata.duration) > 0) { + resolve(true) + } else { + resolve(false) + } + }).catch((err: BusinessError) => { + Logger.error(TAG, `checkIfVideoIsValid fetchMetadata failed, code: ${err.code}, message: ${err.message}`); + resolve(false) + }).finally(() => { + avMetadataExtractor.release(); + fs.closeSync(file); + }); + }); + } + + getSupportedPreviewFpsRange(handle: number): Promise { + Logger.debug("getSupportedPreviewFpsRange") + return new Promise(async (resolve, reject) => { + try { + let cameraManager = camera.getCameraManager(this.ctx.getUIContext().getHostContext()); + let camerasArray = cameraManager?.getSupportedCameras(); + if (!camerasArray) { + Logger.error(TAG, 'getSupportedPreviewFpsRange cannot get cameras'); + reject('getSupportedPreviewFpsRange cannot get cameras') + return; + } + let deviceIndex = handle >= 0 && handle < camerasArray.length ? handle : 0; + let capability = + cameraManager.getSupportedOutputCapability(camerasArray[deviceIndex], camera.SceneMode.NORMAL_VIDEO); + let data: string[] = []; + for (let i = 0; i < capability.videoProfiles.length; i++) { + let fpsRange = capability.videoProfiles[i].frameRateRange; + data.push(`${fpsRange.min},${fpsRange.max}`) + } + resolve(data) + } catch (err) { + let businessError = err as BusinessError; + Logger.error(TAG, + `getSupportedPreviewFpsRange failed, code: ${businessError.code}, message: ${businessError.message}`); + reject(businessError) + } + }); + } + + getSupportedRatios(handle: number): Promise { + Logger.debug("getSupportedRatios") + return new Promise(async (resolve, reject) => { + try { + let cameraManager = camera.getCameraManager(this.ctx.getUIContext().getHostContext()); + let camerasArray = cameraManager?.getSupportedCameras(); + if (!camerasArray) { + Logger.error(TAG, 'getSupportedRatios cannot get cameras'); + reject('getSupportedRatios cannot get cameras') + return; + } + let deviceIndex = handle >= 0 && handle < camerasArray.length ? handle : 0; + let capability = + cameraManager.getSupportedOutputCapability(camerasArray[deviceIndex], camera.SceneMode.NORMAL_VIDEO); + let data: string[] = []; + for (let i = 0; i < capability.previewProfiles.length; i++) { + let w = capability.previewProfiles[i].size.width; + let h = capability.previewProfiles[i].size.height; + // 辗转相除化简为最简整数比,与 Android 上游 "4:3"/"16:9" 输出格式对齐 + let a = w; + let b = h; + while (b !== 0) { + let t = b; + b = a % b; + a = t; + } + let ratio = (w / a) + ':' + (h / a); + if (!data.includes(ratio)) { + data.push(ratio); + } + } + resolve(data) + } catch (err) { + let businessError = err as BusinessError; + Logger.error(TAG, + `getSupportedRatios failed, code: ${businessError.code}, message: ${businessError.message}`); + reject(businessError) + } + }); + } } diff --git a/harmony/reactNativeCamera/src/main/ets/core/CameraConfig.ts b/harmony/reactNativeCamera/src/main/ets/core/CameraConfig.ts index 92211668f..061b33d3b 100644 --- a/harmony/reactNativeCamera/src/main/ets/core/CameraConfig.ts +++ b/harmony/reactNativeCamera/src/main/ets/core/CameraConfig.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { Permissions } from '@kit.AbilityKit'; import { ErrorWithCause } from '../types/CameraError'; import { Orientation } from './CameraEnumBox'; @@ -34,6 +44,8 @@ export interface TakePhotoOptions { fixOrientation?: boolean, //todo 不知道 forceUpOrientation?: boolean, //强制向上方向 pauseAfterCapture?: boolean, //拍摄后暂停 + imageType?: 'jpeg' | 'png', // 4.2.1 新增:拍照输出格式(png 走 ImagePacker 编码) + path?: string, // 4.2.1 新增:自定义保存路径 } export interface PhotoFile { diff --git a/harmony/reactNativeCamera/src/main/ets/core/CameraDeviceInfo.ts b/harmony/reactNativeCamera/src/main/ets/core/CameraDeviceInfo.ts index ec6f147b1..110c3bdcb 100644 --- a/harmony/reactNativeCamera/src/main/ets/core/CameraDeviceInfo.ts +++ b/harmony/reactNativeCamera/src/main/ets/core/CameraDeviceInfo.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import type { AutoFocusSystem, VideoStabilizationMode, @@ -86,7 +96,9 @@ export interface CameraDeviceInfo { export class SimpleCameraDeviceInfo { - id: string + deviceType: string = '' + + id: string = '' - type:number + type: number = 0 } \ No newline at end of file diff --git a/harmony/reactNativeCamera/src/main/ets/core/CameraEnumBox.ts b/harmony/reactNativeCamera/src/main/ets/core/CameraEnumBox.ts index 500b64522..13b764be7 100644 --- a/harmony/reactNativeCamera/src/main/ets/core/CameraEnumBox.ts +++ b/harmony/reactNativeCamera/src/main/ets/core/CameraEnumBox.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + export enum Orientation { PORTRAIT = 'portrait', LANDSCAPE_RIGHT = 'landscapeRight', @@ -49,3 +59,8 @@ export enum cameraState { VIDEO, SCAN } + +export enum ImageType { + JPEG = 'jpeg', + PNG = 'png' +} diff --git a/harmony/reactNativeCamera/src/main/ets/service/CameraManager.ts b/harmony/reactNativeCamera/src/main/ets/service/CameraManager.ts index d66cd6c55..59faa733a 100644 --- a/harmony/reactNativeCamera/src/main/ets/service/CameraManager.ts +++ b/harmony/reactNativeCamera/src/main/ets/service/CameraManager.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import Logger from '../utils/Logger'; import { CameraPermissionRequestResult, CameraPermissionStatus, PermissionArray } from '../core/CameraConfig'; diff --git a/harmony/reactNativeCamera/src/main/ets/service/CameraSession.ts b/harmony/reactNativeCamera/src/main/ets/service/CameraSession.ts index ceb38ddc2..95d14850c 100644 --- a/harmony/reactNativeCamera/src/main/ets/service/CameraSession.ts +++ b/harmony/reactNativeCamera/src/main/ets/service/CameraSession.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import camera from '@ohos.multimedia.camera'; import Logger from '../utils/Logger'; import { PhotoFile, Point, TakePhotoOptions } from '../core/CameraConfig'; @@ -68,6 +78,8 @@ export default class CameraSession { private hasAudio: boolean = false private ctx!: RNOHContext; private cameraDeviceIndex: number = 0; + // 4.2.1 cameraId prop:记录上一次生效的 cameraId,用于检测设备精确切换 + private pendingCameraId: string = ''; private pictureSize: string; public ZoomRange: ZoomRangeType | null = null; public photoPreviewScale: number = 1; @@ -89,6 +101,9 @@ export default class CameraSession { private offsetY: number = 0; public fps: number = 30; private tag: number; + // 4.2.1 新增拍照选项:imageType(jpeg/png)与自定义保存 path + private pendingImageType: string = 'jpeg'; + private pendingPhotoPath: string = ''; constructor(_ctx?: RNOHContext) { _ctx && (this.ctx = _ctx); @@ -139,12 +154,22 @@ export default class CameraSession { const { surfaceId, props, mediaModel } = config; let cameraIndex = 0; props.type === "front" ? cameraIndex = 1 : cameraIndex = 0; - if (this.cameraDeviceIndex === cameraIndex) { + // 4.2.1 cameraId prop:设备精确切换时忽略 type 相同的短路,强制重新 initCamera + let forceReinit = false; + if (props.cameraId && props.cameraId !== '') { + if (this.pendingCameraId !== props.cameraId) { + forceReinit = true; + } + this.pendingCameraId = props.cameraId; + } else { + this.pendingCameraId = ''; + } + if (this.cameraDeviceIndex === cameraIndex && !forceReinit) { return; } this.cameraDeviceIndex = cameraIndex await this.cameraRelease(); - Logger.info(TAG, `changeCameraPosition: ${props.type}`); + Logger.info(TAG, `changeCameraPosition: ${props.type}, cameraId: ${props.cameraId ?? ''}`); this.cameraManager = this.getCameraManagerFn(); this.initCamera(surfaceId, props, mediaModel) } @@ -287,6 +312,17 @@ export default class CameraSession { } else { currentDevice = this.camerasArray[1] } + // cameraId prop 优先:精确匹配 getSupportedCameras 返回的 cameraId + if (props.cameraId && props.cameraId !== '') { + let matchedDevice = this.camerasArray.find(device => { + return device.cameraId === props.cameraId + }) + if (matchedDevice) { + currentDevice = matchedDevice + } else { + Logger.error(TAG, `initCamera cameraId ${props.cameraId} not found, fallback to type selection`); + } + } if (this.mediaModel === camera.SceneMode.NORMAL_PHOTO) { Logger.debug("initPhotoSession") await this.initPhotoSession(currentDevice, surfaceId, props); @@ -772,8 +808,15 @@ export default class CameraSession { await targetSession.stop(); } } catch (error) { - Logger.error(TAG, `The activeChange targetSession start call failed. error code: ${error.code}`); - this.onError(`The activeChange targetSession start call failed. error code: ${error.code}`) + let err = error as BusinessError; + // 7400201(SESSION_ERROR_NOT_CONFIG): initCamera 早于 XComponent surface 创建的启动竞态, + // surface 就绪后的 changeCameraPosition/initCamera 会重新拉起, 不应向 JS 报错 + if (err.code === 7400201) { + Logger.warn(TAG, "activeChange start skipped: session not config yet (surface not ready)"); + return; + } + Logger.error(TAG, `The activeChange targetSession start call failed. error code: ${err.code}`); + this.onError(`The activeChange targetSession start call failed. error code: ${err.code}`) } } @@ -998,8 +1041,11 @@ export default class CameraSession { // 保存图片 async savePicture(photoAccess: photoAccessHelper.PhotoAsset): Promise { - let photoFile = `${this.basicPath}/${this.outPathArray[0]}/${Date.now().toString()}.jpeg`; - // photoFile = await this.getMediaLibraryUri(photoFile, `${Date.now()}`, 'jpeg', photoAccessHelper.PhotoType.IMAGE) + // imageType 拍照选项:支持 png(imageType==='png')与 jpeg 输出;path 拍照选项:自定义保存路径 + let imageType: string = this.pendingImageType === 'png' ? 'png' : 'jpeg'; + let photoFile = this.pendingPhotoPath && this.pendingPhotoPath !== '' ? + this.pendingPhotoPath : + `${this.basicPath}/${this.outPathArray[0]}/${Date.now().toString()}.${imageType}`; // 根据相机拍照图片路径,获取文件buffer let file = fs.openSync(photoAccess.uri, fs.OpenMode.READ_ONLY); let stat = fs.statSync(file.fd); @@ -1007,6 +1053,26 @@ export default class CameraSession { fs.readSync(file.fd, buffer); fs.fsyncSync(file.fd); fs.closeSync(file); + if (imageType === 'png') { + // PNG 输出:解码图片后用 ImagePacker 以 image/png 格式编码 + try { + let imageSource = image.createImageSource(photoAccess.uri); + let pixelMap = await imageSource.createPixelMap(); + let imagePacker = image.createImagePacker(); + let packOpts: image.PackingOption = { format: 'image/png', quality: 100 }; + let packBuffer = await imagePacker.packToData(pixelMap, packOpts); + let _pngFile = fs.openSync(photoFile, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); + await fs.write(_pngFile.fd, packBuffer); + fs.closeSync(_pngFile); + imagePacker.release(); + pixelMap.release(); + imageSource.release(); + this.photoPath = photoFile; + return; + } catch (error) { + Logger.error(TAG, `savePicture png packing failed,code:${JSON.stringify(error)}. fallback to raw copy`); + } + } let _file; try { _file = fs.openSync(photoFile, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); @@ -1184,6 +1250,19 @@ export default class CameraSession { Logger.debug("takePhoto orientation:" + options.orientation) Logger.debug("takePhoto mirrorImage:" + options.mirrorImage) + // 4.2.1 新增:imageType(jpeg/png)与自定义保存 path + if (options) { + if (options.imageType === 'png') { + this.pendingImageType = 'png'; + } else { + this.pendingImageType = 'jpeg'; + } + this.pendingPhotoPath = options.path ?? ''; + } else { + this.pendingImageType = 'jpeg'; + this.pendingPhotoPath = ''; + } + let quality = camera.QualityLevel.QUALITY_LEVEL_MEDIUM; if (options) { if (options.quality) { @@ -1708,7 +1787,13 @@ export default class CameraSession { }); } // 重置 - await this.avRecorder.reset(); + try { + await this.avRecorder.reset(); + } catch (resetError) { + let resetErr = resetError as BusinessError; + // stop 后立即 reset 状态冲突会抛错,不能阻断 onRecordingFinished 回调链 + Logger.error(TAG, `stopRecording: Failed to reset the avRecorder. error: ${JSON.stringify(resetErr)}`); + } // 此处不要释放录制实例,否则需要重新创建 videoOutput 加入 session里,会导致画面闪断 if (this.videoSession.hasFlash() && diff --git a/harmony/reactNativeCamera/src/main/ets/service/ScanSession.ts b/harmony/reactNativeCamera/src/main/ets/service/ScanSession.ts index ad516c7fe..a1caf7fc9 100644 --- a/harmony/reactNativeCamera/src/main/ets/service/ScanSession.ts +++ b/harmony/reactNativeCamera/src/main/ets/service/ScanSession.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { customScan, scanBarcode, scanCore } from '@kit.ScanKit'; import Logger from '../utils/Logger'; import { BusinessError } from '@ohos.base'; diff --git a/harmony/reactNativeCamera/src/main/ets/types/AuthType.ts b/harmony/reactNativeCamera/src/main/ets/types/AuthType.ts index c84a7cd81..c34b591b6 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/AuthType.ts +++ b/harmony/reactNativeCamera/src/main/ets/types/AuthType.ts @@ -1,26 +1,18 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2025 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + export enum AuthType { READY, PENDING_AUTHORIZATION, diff --git a/harmony/reactNativeCamera/src/main/ets/types/CameraError.ts b/harmony/reactNativeCamera/src/main/ets/types/CameraError.ts index 463db7b5f..9a3ecdfd8 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/CameraError.ts +++ b/harmony/reactNativeCamera/src/main/ets/types/CameraError.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + export type PermissionError = 'permission/microphone-permission-denied' | 'permission/camera-permission-denied' export type ParameterError = | 'parameter/invalid-parameter' diff --git a/harmony/reactNativeCamera/src/main/ets/types/ReactNativeCameraProps.ets b/harmony/reactNativeCamera/src/main/ets/types/ReactNativeCameraProps.ets index 9b7695890..0a859da23 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/ReactNativeCameraProps.ets +++ b/harmony/reactNativeCamera/src/main/ets/types/ReactNativeCameraProps.ets @@ -1,26 +1,18 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2025 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { ViewBaseProps } from "@rnoh/react-native-openharmony" export interface FocusPoint { @@ -45,6 +37,7 @@ export interface ReactNativeCameraProps extends ViewBaseProps { showsUserLocation: boolean, autoFocus: string | number, autoFocusPointOfInterest: FocusPoint, + cameraId: string, pictureSize: string, type: string, flashMode: string, @@ -66,6 +59,7 @@ export interface ReactNativeCameraProps extends ViewBaseProps { ratio: string, whiteBalance: string, playSoundOnCapture: boolean, + playSoundOnRecord: boolean, videoStabilizationMode: string, defaultVideoQuality: string, keepAudioSession: boolean diff --git a/harmony/reactNativeCamera/src/main/ets/types/TempCameraProp.ts b/harmony/reactNativeCamera/src/main/ets/types/TempCameraProp.ts index 496e85011..11fb06491 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/TempCameraProp.ts +++ b/harmony/reactNativeCamera/src/main/ets/types/TempCameraProp.ts @@ -1,26 +1,18 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2025 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + interface FocusPoint { x: number, y: number @@ -43,6 +35,7 @@ export class TempCameraProps { showsUserLocation: boolean = false; autoFocus: string | number = ""; autoFocusPointOfInterest: FocusPoint = null; + cameraId: string = ""; pictureSize: string = ""; type: string = "back"; flashMode: string = ""; @@ -63,6 +56,7 @@ export class TempCameraProps { trackingEnabled: boolean = false; ratio: string = ""; playSoundOnCapture: boolean = false; + playSoundOnRecord: boolean = false; videoStabilizationMode: string = ""; defaultVideoQuality: string = ""; keepAudioSession: boolean = false diff --git a/harmony/reactNativeCamera/src/main/ets/types/TemporaryFile.ts b/harmony/reactNativeCamera/src/main/ets/types/TemporaryFile.ts index e0ae97f55..66660a94c 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/TemporaryFile.ts +++ b/harmony/reactNativeCamera/src/main/ets/types/TemporaryFile.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + export interface TemporaryFile { path: string } diff --git a/harmony/reactNativeCamera/src/main/ets/types/VideoFile.ts b/harmony/reactNativeCamera/src/main/ets/types/VideoFile.ts index 2f5722c98..4746b0b88 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/VideoFile.ts +++ b/harmony/reactNativeCamera/src/main/ets/types/VideoFile.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { Orientation } from '../core/CameraEnumBox'; import type { CameraCaptureError } from './CameraError' import type { TemporaryFile } from './TemporaryFile' diff --git a/harmony/reactNativeCamera/src/main/ets/types/VisionCameraModuleSpec.ts b/harmony/reactNativeCamera/src/main/ets/types/VisionCameraModuleSpec.ts index 9ce9df7a6..389ff66ed 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/VisionCameraModuleSpec.ts +++ b/harmony/reactNativeCamera/src/main/ets/types/VisionCameraModuleSpec.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { Tag } from "@rnoh/react-native-openharmony/ts" export namespace VisionCameraModuleSpec { diff --git a/harmony/reactNativeCamera/src/main/ets/types/VisionCameraViewSpec.ts b/harmony/reactNativeCamera/src/main/ets/types/VisionCameraViewSpec.ts index 9ca023187..3424f6ab8 100644 --- a/harmony/reactNativeCamera/src/main/ets/types/VisionCameraViewSpec.ts +++ b/harmony/reactNativeCamera/src/main/ets/types/VisionCameraViewSpec.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { Descriptor as ComponentDescriptor, ViewBaseProps, diff --git a/harmony/reactNativeCamera/src/main/ets/utils/Logger.ts b/harmony/reactNativeCamera/src/main/ets/utils/Logger.ts index 0b78beb29..68c2f8d30 100644 --- a/harmony/reactNativeCamera/src/main/ets/utils/Logger.ts +++ b/harmony/reactNativeCamera/src/main/ets/utils/Logger.ts @@ -1,10 +1,20 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import hilog from '@ohos.hilog'; class Logger { diff --git a/harmony/reactNativeCamera/src/main/ets/utils/OrientationUtils.ets b/harmony/reactNativeCamera/src/main/ets/utils/OrientationUtils.ets index 10eb69c09..762de2e3a 100644 --- a/harmony/reactNativeCamera/src/main/ets/utils/OrientationUtils.ets +++ b/harmony/reactNativeCamera/src/main/ets/utils/OrientationUtils.ets @@ -1,26 +1,18 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2025 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import { display } from '@kit.ArkUI'; import { Orientation } from '../core/CameraEnumBox'; import Logger from '../Logger'; diff --git a/harmony/reactNativeCamera/src/main/ets/utils/PermissionUtils.ts b/harmony/reactNativeCamera/src/main/ets/utils/PermissionUtils.ts index 544ac915a..5c59658b6 100644 --- a/harmony/reactNativeCamera/src/main/ets/utils/PermissionUtils.ts +++ b/harmony/reactNativeCamera/src/main/ets/utils/PermissionUtils.ts @@ -1,10 +1,20 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + import abilityAccessCtrl, { PermissionRequestResult } from '@ohos.abilityAccessCtrl'; import { Permissions } from '@ohos.abilityAccessCtrl'; import bundleManager from '@ohos.bundle.bundleManager'; diff --git a/harmony/reactNativeCamera/src/main/ets/utils/Utils.ts b/harmony/reactNativeCamera/src/main/ets/utils/Utils.ts index f6909f4f6..e3b05c308 100644 --- a/harmony/reactNativeCamera/src/main/ets/utils/Utils.ts +++ b/harmony/reactNativeCamera/src/main/ets/utils/Utils.ts @@ -1,9 +1,19 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved - * Use of this source code is governed by a MIT license that can be - * found in the LICENSE file. + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + const TAG = 'CameraUtils' const Utils = { diff --git a/harmony/reactNativeCamera/ts.ets b/harmony/reactNativeCamera/ts.ets index 15438a215..3a227857c 100644 --- a/harmony/reactNativeCamera/ts.ets +++ b/harmony/reactNativeCamera/ts.ets @@ -1,27 +1,19 @@ -/** - * MIT License +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the MIT License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * https://github.com/react-native-camera/react-native-camera/blob/v4.2.1/LICENSE * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + export * from "./src/main/ets/FaceDectorPackage" export * from "./src/main/ets/FaceDectorTurboModule" export * from "./src/main/ets/ReactNativeCameraPackage" diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..f5a5204d4 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * 库根薄壳 jest 配置:转发到 jest/jest.config.js(真正的配置与注释在那里)。 + * Jest CLI 从库根运行时只自动发现库根的 jest.config.js。 + */ + +module.exports = require("./jest/jest.config.js"); diff --git a/jest/__tests__/constants-defaultprops-test.js b/jest/__tests__/constants-defaultprops-test.js new file mode 100644 index 000000000..f0e5a296b --- /dev/null +++ b/jest/__tests__/constants-defaultprops-test.js @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * RNCamera.js 的 Constants 与 defaultProps 形状白盒测试。 + * 重点锁定 harmony 适配的枚举常量字符串化(JS stub 常量须与 cpp 枚举解析一致)。 + */ + +jest.mock("react", () => require("../helpers/reactMock")); +jest.mock("prop-types", () => require("../helpers/propTypesMock")); +jest.mock("react-native", () => require("../helpers/reactNativeMock")); +jest.mock("../../src/NativeCamera", () => require("../helpers/nativeCameraMock")); +jest.mock("../../src/NativeCameraModule", () => require("../helpers/nativeCameraModuleMock")); +jest.mock("../../src/NativeFaceDetector", () => require("../helpers/nativeFaceDetectorMock")); +jest.mock("../../src/FaceDetector", () => { + const Native = require("../helpers/nativeFaceDetectorMock"); + class FaceDetector {} + FaceDetector.Constants = { + Mode: {}, + Landmarks: {}, + Classifications: {}, + }; + FaceDetector.detectFacesAsync = jest.fn(); + return { __esModule: true, default: FaceDetector, Constants: FaceDetector.Constants }; +}); + +const { RNCamera } = require("../../src/index"); + +describe("Constants 形状(harmony 枚举字符串化锁定)", () => { + const { Constants } = RNCamera; + + it("Type 为字符串枚举", () => { + expect(Constants.Type.back).toBe("back"); + expect(Constants.Type.front).toBe("front"); + }); + + it("AutoFocus 为字符串枚举", () => { + expect(Constants.AutoFocus.on).toBe("on"); + expect(Constants.AutoFocus.off).toBe("off"); + }); + + it("FlashMode 为字符串枚举(4 值)", () => { + expect(Constants.FlashMode).toEqual({ off: "off", on: "on", torch: "torch", auto: "auto" }); + }); + + it("WhiteBalance 为字符串枚举(6 值)", () => { + expect(Constants.WhiteBalance).toEqual({ + sunny: "sunny", + cloudy: "cloudy", + shadow: "shadow", + incandescent: "incandescent", + fluorescent: "fluorescent", + auto: "auto", + }); + }); + + it("GoogleVisionBarcodeDetection 形状", () => { + expect(Constants.GoogleVisionBarcodeDetection).toEqual({ BarcodeType: 0, BarcodeMode: 0 }); + }); + + it("视频稳定枚举默认值 stub 存在", () => { + expect(Constants.VideoStabilization).toBeDefined(); + }); +}); + +describe("defaultProps 形状", () => { + const { RNCamera } = require("../../src/index"); + const defaultProps = RNCamera.defaultProps; + + it("cameraId 默认空字符串(4.2.1 语义:null -> '')", () => { + expect(defaultProps.cameraId).toBe(""); + }); + + it("type 默认 back", () => { + expect(defaultProps.type).toBe("back"); + }); + + it("playSoundOnRecord 默认 false", () => { + expect(defaultProps.playSoundOnRecord).toBe(false); + }); + + it("captureAudio 默认 true", () => { + expect(defaultProps.captureAudio).toBe(true); + }); + + it("枚举类默认值取自字符串常量", () => { + expect(defaultProps.autoFocus).toBe("on"); + expect(defaultProps.flashMode).toBe("off"); + expect(defaultProps.whiteBalance).toBe("auto"); + }); + + it("常规默认值", () => { + expect(defaultProps.ratio).toBe("4:3"); + expect(defaultProps.pictureSize).toBe("None"); + expect(defaultProps.playSoundOnCapture).toBe(false); + expect(defaultProps.videoStabilizationMode).toBe("off"); + }); +}); diff --git a/jest/__tests__/events-test.js b/jest/__tests__/events-test.js new file mode 100644 index 000000000..a07cd71db --- /dev/null +++ b/jest/__tests__/events-test.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * RNCamera 事件处理白盒测试: + * - _onObjectDetected 的事件去重节流(同 payload 在 EventThrottleMs 内不重复回调) + * - _onTouch 的 onTap / onDoubleTap 分发(isDoubleTap 标志区分) + */ + +jest.mock("react", () => require("../helpers/reactMock")); +jest.mock("prop-types", () => require("../helpers/propTypesMock")); +jest.mock("react-native", () => require("../helpers/reactNativeMock")); +jest.mock("../../src/NativeCamera", () => require("../helpers/nativeCameraMock")); +jest.mock("../../src/NativeCameraModule", () => require("../helpers/nativeCameraModuleMock")); +jest.mock("../../src/NativeFaceDetector", () => require("../helpers/nativeFaceDetectorMock")); +jest.mock("../../src/FaceDetector", () => { + const Native = require("../helpers/nativeFaceDetectorMock"); + class FaceDetector {} + FaceDetector.Constants = { Mode: {}, Landmarks: {}, Classifications: {} }; + FaceDetector.detectFacesAsync = jest.fn(); + return { __esModule: true, default: FaceDetector, Constants: FaceDetector.Constants }; +}); + +const { RNCamera } = require("../../src/index"); + +function makeEvent(type, payload) { + return { type, ...payload }; +} + +describe("_onObjectDetected 节流", () => { + it("相同 payload 在节流窗口内只回调一次", () => { + const cam = new RNCamera({}); + const cb = jest.fn(); + const handler = cam._onObjectDetected(cb); + const nativeEvent = makeEvent("onBarCodeRead", { data: "qr-1" }); + handler({ nativeEvent }); + handler({ nativeEvent }); + expect(cb).toHaveBeenCalledTimes(1); + }); + + it("payload 变化后再次回调", () => { + const cam = new RNCamera({}); + const cb = jest.fn(); + const handler = cam._onObjectDetected(cb); + handler({ nativeEvent: makeEvent("onBarCodeRead", { data: "qr-1" }) }); + handler({ nativeEvent: makeEvent("onBarCodeRead", { data: "qr-2" }) }); + expect(cb).toHaveBeenCalledTimes(2); + }); + + it("callback 为空时不写入节流缓存", () => { + const cam = new RNCamera({}); + const handler = cam._onObjectDetected(undefined); + expect(() => handler({ nativeEvent: makeEvent("onBarCodeRead", { data: "x" }) })).not.toThrow(); + }); +}); + +describe("_onTouch 分发", () => { + it("单击分发 onTap", () => { + const cam = new RNCamera({}); + const onTap = jest.fn(); + const onDoubleTap = jest.fn(); + cam.props = { onTap, onDoubleTap }; + cam._onTouch({ nativeEvent: { isDoubleTap: false, touchOrigin: { x: 1, y: 2 } } }); + expect(onTap).toHaveBeenCalledWith({ x: 1, y: 2 }); + expect(onDoubleTap).not.toHaveBeenCalled(); + }); + + it("双击分发 onDoubleTap", () => { + const cam = new RNCamera({}); + const onTap = jest.fn(); + const onDoubleTap = jest.fn(); + cam.props = { onTap, onDoubleTap }; + cam._onTouch({ nativeEvent: { isDoubleTap: true, touchOrigin: { x: 3, y: 4 } } }); + expect(onDoubleTap).toHaveBeenCalledWith({ x: 3, y: 4 }); + expect(onTap).not.toHaveBeenCalled(); + }); + + it("未传回调时不抛错", () => { + const cam = new RNCamera({}); + cam.props = {}; + expect(() => + cam._onTouch({ nativeEvent: { isDoubleTap: false, touchOrigin: { x: 0, y: 0 } } }) + ).not.toThrow(); + }); +}); diff --git a/jest/__tests__/facedetector-test.js b/jest/__tests__/facedetector-test.js new file mode 100644 index 000000000..78b1e837f --- /dev/null +++ b/jest/__tests__/facedetector-test.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * FaceDetector.js 白盒测试: + * - detectFacesAsync 的参数合并({...options, uri}) + * - NativeTurboModule 缺失时的 stub 兜底(detectFaces reject) + */ + +jest.mock("../../src/NativeFaceDetector", () => require("../helpers/nativeFaceDetectorMock")); + +const NativeFaceDetector = require("../../src/NativeFaceDetector"); +const FaceDetector = require("../../src/FaceDetector").default; + +describe("FaceDetector(注入 stub)", () => { + it("detectFacesAsync 合并 uri 与 options", async () => { + NativeFaceDetector.detectFaces.mockResolvedValueOnce([]); + const out = await FaceDetector.detectFacesAsync("file:///f.jpg", { mode: "fast" }); + expect(out).toEqual([]); + expect(NativeFaceDetector.detectFaces).toHaveBeenCalledWith({ mode: "fast", uri: "file:///f.jpg" }); + }); + + it("Constants 形状透传", () => { + expect(FaceDetector.Constants).toBeDefined(); + expect(typeof FaceDetector.detectFacesAsync).toBe("function"); + }); + +}); diff --git a/jest/__tests__/index-test.js b/jest/__tests__/index-test.js new file mode 100644 index 000000000..52b7d4fdd --- /dev/null +++ b/jest/__tests__/index-test.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * index.js 白盒测试(exports 形状与再导出透传)。 + */ + +jest.mock("react", () => require("../helpers/reactMock")); +jest.mock("prop-types", () => require("../helpers/propTypesMock")); +jest.mock("react-native", () => require("../helpers/reactNativeMock")); +jest.mock("../../src/NativeCamera", () => require("../helpers/nativeCameraMock")); +jest.mock("../../src/NativeCameraModule", () => require("../helpers/nativeCameraModuleMock")); +jest.mock("../../src/NativeFaceDetector", () => require("../helpers/nativeFaceDetectorMock")); +jest.mock("../../src/FaceDetector", () => { + const Native = require("../helpers/nativeFaceDetectorMock"); + class FaceDetector {} + FaceDetector.Constants = { + Mode: Native.Mode, + Landmarks: Native.Landmarks, + Classifications: Native.Classifications, + }; + FaceDetector.detectFacesAsync = jest.fn(); + return { __esModule: true, default: FaceDetector, Constants: FaceDetector.Constants }; +}); + +const index = require("../../src/index"); +const nativeModuleMock = require("../helpers/nativeCameraModuleMock"); + +describe("index exports 形状", () => { + it("导出 RNCamera(类)", () => { + expect(typeof index.RNCamera).toBe("function"); + expect(index.RNCamera.displayName).toBeUndefined(); + expect(index.RNCamera.prototype).toBeDefined(); + }); + + it("导出 FaceDetector(类)", () => { + expect(typeof index.FaceDetector).toBe("function"); + }); + + it("导出 hasTorch(函数)", () => { + expect(typeof index.hasTorch).toBe("function"); + }); + + it("RNCamera.Constants 形状(Type/AutoFocus/FlashMode 字符串化)", () => { + expect(index.RNCamera.Constants.Type.back).toBe("back"); + expect(index.RNCamera.Constants.FlashMode.off).toBe("off"); + }); + + it("hasTorch 调用透传到 harmony 分支的 NativeCameraModule", async () => { + nativeModuleMock.hasTorch.mockResolvedValueOnce(false); + await expect(index.hasTorch()).resolves.toBe(false); + expect(nativeModuleMock.hasTorch).toHaveBeenCalledTimes(1); + }); +}); diff --git a/jest/__tests__/platform-routing-test.js b/jest/__tests__/platform-routing-test.js new file mode 100644 index 000000000..b156bd7d4 --- /dev/null +++ b/jest/__tests__/platform-routing-test.js @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * 平台路由白盒测试:4.2.1 新增接口的 harmony 分支透传与 iOS 分支行为。 + * 通过 RNCameraJestMock.setPlatformOS 在文件内切换平台态。 + */ + +jest.mock("react", () => require("../helpers/reactMock")); +jest.mock("prop-types", () => require("../helpers/propTypesMock")); +jest.mock("react-native", () => require("../helpers/reactNativeMock")); +jest.mock("../../src/NativeCamera", () => require("../helpers/nativeCameraMock")); +jest.mock("../../src/NativeCameraModule", () => require("../helpers/nativeCameraModuleMock")); +jest.mock("../../src/NativeFaceDetector", () => require("../helpers/nativeFaceDetectorMock")); +jest.mock("../../src/FaceDetector", () => { + const Native = require("../helpers/nativeFaceDetectorMock"); + class FaceDetector {} + FaceDetector.Constants = { Mode: {}, Landmarks: {}, Classifications: {} }; + FaceDetector.detectFacesAsync = jest.fn(); + return { __esModule: true, default: FaceDetector, Constants: FaceDetector.Constants }; +}); + +const { RNCamera, hasTorch } = require("../../src/index"); +const NativeCameraModule = require("../../src/NativeCameraModule"); +const { RNCameraJestMock } = require("../helpers/reactNativeMock"); + +describe("harmony 平台路由(4.2.1 新增接口)", () => { + afterEach(() => { + jest.clearAllMocks(); + RNCameraJestMock.setPlatformOS("harmony"); + }); + + it("hasTorch 透传 NativeCameraModule.hasTorch", async () => { + NativeCameraModule.hasTorch.mockResolvedValueOnce(true); + await expect(hasTorch()).resolves.toBe(true); + expect(NativeCameraModule.hasTorch).toHaveBeenCalledTimes(1); + }); + + it("checkIfVideoIsValid 透传路径参数", async () => { + NativeCameraModule.checkIfVideoIsValid.mockResolvedValueOnce(false); + await expect(RNCamera.checkIfVideoIsValid("/tmp/v.mp4")).resolves.toBe(false); + expect(NativeCameraModule.checkIfVideoIsValid).toHaveBeenCalledWith("/tmp/v.mp4"); + }); + + it("getSupportedPreviewFpsRange 透传 handle", async () => { + NativeCameraModule.getSupportedPreviewFpsRange.mockResolvedValueOnce(["15,30"]); + const cam = new RNCamera({}); + cam._cameraHandle = 7; + await expect(cam.getSupportedPreviewFpsRange()).resolves.toEqual(["15,30"]); + expect(NativeCameraModule.getSupportedPreviewFpsRange).toHaveBeenCalledWith(7); + }); + + it("getSupportedRatiosAsync 透传 handle", async () => { + NativeCameraModule.getSupportedRatios.mockResolvedValueOnce(["4:3"]); + const cam = new RNCamera({}); + cam._cameraHandle = 3; + await expect(cam.getSupportedRatiosAsync()).resolves.toEqual(["4:3"]); + expect(NativeCameraModule.getSupportedRatios).toHaveBeenCalledWith(3); + }); + + it("getAvailablePictureSizes 透传", async () => { + NativeCameraModule.getAvailablePictureSizes.mockResolvedValueOnce(["640x480"]); + const cam = new RNCamera({}); + await expect(cam.getAvailablePictureSizes()).resolves.toEqual(["640x480"]); + expect(NativeCameraModule.getAvailablePictureSizes).toHaveBeenCalledTimes(1); + }); + + it("getCameraIdsAsync 走 harmony 分支", async () => { + NativeCameraModule.getCameraIds.mockResolvedValueOnce([{ id: "camX", type: 1 }]); + const cam = new RNCamera({}); + const data = await cam.getCameraIdsAsync(); + expect(data).toEqual([{ id: "camX", type: 1 }]); + expect(NativeCameraModule.getCameraIds).toHaveBeenCalledTimes(1); + }); +}); + +describe("iOS 平台分支", () => { + afterEach(() => { + jest.clearAllMocks(); + RNCameraJestMock.setPlatformOS("harmony"); + }); + + it("checkIfVideoIsValid 在 iOS 短路返回 true 且不调 native", async () => { + RNCameraJestMock.setPlatformOS("ios"); + await expect(RNCamera.checkIfVideoIsValid("/tmp/x.mp4")).resolves.toBe(true); + expect(NativeCameraModule.checkIfVideoIsValid).not.toHaveBeenCalled(); + }); + + it("getSupportedPreviewFpsRange 在 iOS 抛不支持错误", async () => { + RNCameraJestMock.setPlatformOS("ios"); + const cam = new RNCamera({}); + await expect(cam.getSupportedPreviewFpsRange()).rejects.toThrow("not supported on iOS"); + expect(NativeCameraModule.getSupportedPreviewFpsRange).not.toHaveBeenCalled(); + }); + + it("getSupportedRatiosAsync 在 iOS 抛不支持错误", async () => { + RNCameraJestMock.setPlatformOS("ios"); + const cam = new RNCamera({}); + await expect(cam.getSupportedRatiosAsync()).rejects.toThrow("Ratio is not supported on iOS"); + expect(NativeCameraModule.getSupportedRatios).not.toHaveBeenCalled(); + }); +}); diff --git a/jest/babel.config.js b/jest/babel.config.js new file mode 100644 index 000000000..aebffbd46 --- /dev/null +++ b/jest/babel.config.js @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * Babel 配置(rnoh-js-test skill 配置三件套的对齐产物)。 + * + * jest.config.js 的 transform 已给 babel-jest 内联了同样的 presets/plugins + * (并显式 configFile: false / babelrc: false),因此 jest 运行时不会加载本文件。 + * 本文件作用:对齐 skill 配置产物形态,供未来独立 babel 转译脚本复用。 + */ + +module.exports = { + plugins: [ + ["babel-plugin-syntax-hermes-parser", { parseLangTypes: "flow" }], + ], + presets: [ + ["@babel/preset-env", { targets: { node: "current" }, modules: "commonjs" }], + ["@babel/preset-flow", { all: true }], + ], +}; diff --git a/jest/coverage/clover.xml b/jest/coverage/clover.xml new file mode 100644 index 000000000..e102c53e8 --- /dev/null +++ b/jest/coverage/clover.xml @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jest/coverage/coverage-final.json b/jest/coverage/coverage-final.json new file mode 100644 index 000000000..2039d3b92 --- /dev/null +++ b/jest/coverage/coverage-final.json @@ -0,0 +1,4 @@ +{"D:\\work_file\\tpc\\react-native-camera\\react-native-camera-sig\\src\\FaceDetector.js": {"path":"D:\\work_file\\tpc\\react-native-camera\\react-native-camera-sig\\src\\FaceDetector.js","statementMap":{"0":{"start":{"line":4,"column":37},"end":{"line":4,"column":90}},"1":{"start":{"line":6,"column":35},"end":{"line":12,"column":1}},"2":{"start":{"line":11,"column":21},"end":{"line":11,"column":85}},"3":{"start":{"line":11,"column":48},"end":{"line":11,"column":84}},"4":{"start":{"line":49,"column":21},"end":{"line":53,"column":3}},"5":{"start":{"line":56,"column":4},"end":{"line":56,"column":63}},"6":{"start":{"line":60,"column":25},"end":{"line":60,"column":47}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":11,"column":15},"end":{"line":11,"column":16}},"loc":{"start":{"line":11,"column":21},"end":{"line":11,"column":85}},"line":11},"1":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":33},"end":{"line":11,"column":34}},"loc":{"start":{"line":11,"column":48},"end":{"line":11,"column":84}},"line":11},"2":{"name":"(anonymous_2)","decl":{"start":{"line":55,"column":2},"end":{"line":55,"column":3}},"loc":{"start":{"line":55,"column":96},"end":{"line":57,"column":3}},"line":55}},"branchMap":{"0":{"loc":{"start":{"line":6,"column":35},"end":{"line":12,"column":1}},"type":"binary-expr","locations":[{"start":{"line":6,"column":35},"end":{"line":6,"column":52}},{"start":{"line":6,"column":56},"end":{"line":12,"column":1}}],"line":6}},"s":{"0":1,"1":1,"2":0,"3":0,"4":1,"5":1,"6":1},"f":{"0":0,"1":0,"2":1},"b":{"0":[1,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"8b13d8069c8b977e3bd35f0d68d599043abe0aac"} +,"D:\\work_file\\tpc\\react-native-camera\\react-native-camera-sig\\src\\RNCamera.js": {"path":"D:\\work_file\\tpc\\react-native-camera\\react-native-camera-sig\\src\\RNCamera.js","statementMap":{"0":{"start":{"line":19,"column":18},"end":{"line":25,"column":2}},"1":{"start":{"line":27,"column":27},"end":{"line":86,"column":1}},"2":{"start":{"line":33,"column":29},"end":{"line":33,"column":34}},"3":{"start":{"line":34,"column":34},"end":{"line":34,"column":39}},"4":{"start":{"line":35,"column":2},"end":{"line":51,"column":3}},"5":{"start":{"line":36,"column":4},"end":{"line":36,"column":79}},"6":{"start":{"line":37,"column":9},"end":{"line":51,"column":3}},"7":{"start":{"line":38,"column":35},"end":{"line":41,"column":5}},"8":{"start":{"line":42,"column":4},"end":{"line":46,"column":5}},"9":{"start":{"line":43,"column":6},"end":{"line":43,"column":52}},"10":{"start":{"line":45,"column":6},"end":{"line":45,"column":91}},"11":{"start":{"line":47,"column":9},"end":{"line":51,"column":3}},"12":{"start":{"line":48,"column":4},"end":{"line":48,"column":77}},"13":{"start":{"line":49,"column":9},"end":{"line":51,"column":3}},"14":{"start":{"line":50,"column":4},"end":{"line":50,"column":74}},"15":{"start":{"line":53,"column":2},"end":{"line":80,"column":3}},"16":{"start":{"line":54,"column":4},"end":{"line":79,"column":5}},"17":{"start":{"line":55,"column":6},"end":{"line":55,"column":92}},"18":{"start":{"line":56,"column":11},"end":{"line":79,"column":5}},"19":{"start":{"line":57,"column":6},"end":{"line":74,"column":7}},"20":{"start":{"line":58,"column":38},"end":{"line":61,"column":9}},"21":{"start":{"line":62,"column":8},"end":{"line":66,"column":9}},"22":{"start":{"line":63,"column":10},"end":{"line":63,"column":60}},"23":{"start":{"line":65,"column":10},"end":{"line":65,"column":99}},"24":{"start":{"line":67,"column":13},"end":{"line":74,"column":7}},"25":{"start":{"line":69,"column":8},"end":{"line":73,"column":10}},"26":{"start":{"line":75,"column":11},"end":{"line":79,"column":5}},"27":{"start":{"line":76,"column":6},"end":{"line":76,"column":84}},"28":{"start":{"line":77,"column":11},"end":{"line":79,"column":5}},"29":{"start":{"line":78,"column":6},"end":{"line":78,"column":79}},"30":{"start":{"line":82,"column":2},"end":{"line":85,"column":4}},"31":{"start":{"line":88,"column":15},"end":{"line":98,"column":2}},"32":{"start":{"line":317,"column":48},"end":{"line":321,"column":1}},"33":{"start":{"line":327,"column":4},"end":{"line":331,"column":1}},"34":{"start":{"line":333,"column":30},"end":{"line":395,"column":1}},"35":{"start":{"line":397,"column":24},"end":{"line":397,"column":27}},"36":{"start":{"line":399,"column":18},"end":{"line":405,"column":1}},"37":{"start":{"line":400,"column":17},"end":{"line":400,"column":19}},"38":{"start":{"line":401,"column":2},"end":{"line":403,"column":5}},"39":{"start":{"line":402,"column":4},"end":{"line":402,"column":37}},"40":{"start":{"line":404,"column":2},"end":{"line":404,"column":16}},"41":{"start":{"line":408,"column":21},"end":{"line":430,"column":3}},"42":{"start":{"line":433,"column":28},"end":{"line":445,"column":3}},"43":{"start":{"line":447,"column":21},"end":{"line":506,"column":3}},"44":{"start":{"line":509,"column":32},"end":{"line":548,"column":3}},"45":{"start":{"line":550,"column":61},"end":{"line":550,"column":65}},"46":{"start":{"line":557,"column":4},"end":{"line":557,"column":17}},"47":{"start":{"line":558,"column":4},"end":{"line":558,"column":26}},"48":{"start":{"line":559,"column":4},"end":{"line":559,"column":31}},"49":{"start":{"line":560,"column":4},"end":{"line":560,"column":27}},"50":{"start":{"line":561,"column":4},"end":{"line":565,"column":6}},"51":{"start":{"line":569,"column":4},"end":{"line":571,"column":5}},"52":{"start":{"line":570,"column":6},"end":{"line":570,"column":19}},"53":{"start":{"line":572,"column":4},"end":{"line":574,"column":5}},"54":{"start":{"line":573,"column":6},"end":{"line":573,"column":26}},"55":{"start":{"line":576,"column":4},"end":{"line":587,"column":5}},"56":{"start":{"line":577,"column":6},"end":{"line":586,"column":7}},"57":{"start":{"line":578,"column":32},"end":{"line":578,"column":39}},"58":{"start":{"line":579,"column":8},"end":{"line":579,"column":69}},"59":{"start":{"line":580,"column":8},"end":{"line":585,"column":9}},"60":{"start":{"line":581,"column":10},"end":{"line":584,"column":11}},"61":{"start":{"line":583,"column":12},"end":{"line":583,"column":69}},"62":{"start":{"line":589,"column":4},"end":{"line":591,"column":5}},"63":{"start":{"line":590,"column":6},"end":{"line":590,"column":40}},"64":{"start":{"line":593,"column":4},"end":{"line":595,"column":5}},"65":{"start":{"line":594,"column":6},"end":{"line":594,"column":43}},"66":{"start":{"line":597,"column":4},"end":{"line":599,"column":5}},"67":{"start":{"line":598,"column":6},"end":{"line":598,"column":43}},"68":{"start":{"line":601,"column":4},"end":{"line":614,"column":7}},"69":{"start":{"line":602,"column":6},"end":{"line":613,"column":7}},"70":{"start":{"line":603,"column":38},"end":{"line":606,"column":10}},"71":{"start":{"line":604,"column":10},"end":{"line":604,"column":24}},"72":{"start":{"line":605,"column":10},"end":{"line":605,"column":41}},"73":{"start":{"line":608,"column":8},"end":{"line":608,"column":54}},"74":{"start":{"line":611,"column":8},"end":{"line":611,"column":18}},"75":{"start":{"line":612,"column":8},"end":{"line":612,"column":15}},"76":{"start":{"line":618,"column":4},"end":{"line":624,"column":5}},"77":{"start":{"line":619,"column":6},"end":{"line":619,"column":67}},"78":{"start":{"line":620,"column":11},"end":{"line":624,"column":5}},"79":{"start":{"line":621,"column":6},"end":{"line":621,"column":77}},"80":{"start":{"line":623,"column":6},"end":{"line":623,"column":55}},"81":{"start":{"line":628,"column":4},"end":{"line":643,"column":5}},"82":{"start":{"line":629,"column":6},"end":{"line":629,"column":66}},"83":{"start":{"line":630,"column":11},"end":{"line":643,"column":5}},"84":{"start":{"line":631,"column":6},"end":{"line":631,"column":48}},"85":{"start":{"line":632,"column":11},"end":{"line":643,"column":5}},"86":{"start":{"line":633,"column":6},"end":{"line":642,"column":9}},"87":{"start":{"line":634,"column":8},"end":{"line":641,"column":9}},"88":{"start":{"line":635,"column":21},"end":{"line":635,"column":60}},"89":{"start":{"line":636,"column":10},"end":{"line":636,"column":23}},"90":{"start":{"line":639,"column":10},"end":{"line":639,"column":20}},"91":{"start":{"line":640,"column":10},"end":{"line":640,"column":17}},"92":{"start":{"line":647,"column":4},"end":{"line":653,"column":5}},"93":{"start":{"line":648,"column":6},"end":{"line":648,"column":59}},"94":{"start":{"line":649,"column":11},"end":{"line":653,"column":5}},"95":{"start":{"line":650,"column":6},"end":{"line":650,"column":64}},"96":{"start":{"line":652,"column":6},"end":{"line":652,"column":18}},"97":{"start":{"line":656,"column":32},"end":{"line":664,"column":3}},"98":{"start":{"line":657,"column":4},"end":{"line":663,"column":5}},"99":{"start":{"line":658,"column":6},"end":{"line":658,"column":76}},"100":{"start":{"line":659,"column":11},"end":{"line":663,"column":5}},"101":{"start":{"line":660,"column":6},"end":{"line":660,"column":86}},"102":{"start":{"line":662,"column":6},"end":{"line":662,"column":77}},"103":{"start":{"line":666,"column":29},"end":{"line":669,"column":3}},"104":{"start":{"line":668,"column":4},"end":{"line":668,"column":63}},"105":{"start":{"line":672,"column":4},"end":{"line":676,"column":5}},"106":{"start":{"line":673,"column":6},"end":{"line":673,"column":19}},"107":{"start":{"line":674,"column":11},"end":{"line":676,"column":5}},"108":{"start":{"line":675,"column":6},"end":{"line":675,"column":71}},"109":{"start":{"line":677,"column":4},"end":{"line":688,"column":5}},"110":{"start":{"line":678,"column":6},"end":{"line":687,"column":7}},"111":{"start":{"line":679,"column":32},"end":{"line":679,"column":39}},"112":{"start":{"line":680,"column":8},"end":{"line":680,"column":69}},"113":{"start":{"line":681,"column":8},"end":{"line":686,"column":9}},"114":{"start":{"line":682,"column":10},"end":{"line":685,"column":11}},"115":{"start":{"line":684,"column":12},"end":{"line":684,"column":69}},"116":{"start":{"line":690,"column":4},"end":{"line":695,"column":5}},"117":{"start":{"line":691,"column":6},"end":{"line":694,"column":7}},"118":{"start":{"line":693,"column":8},"end":{"line":693,"column":67}},"119":{"start":{"line":697,"column":44},"end":{"line":697,"column":54}},"120":{"start":{"line":698,"column":29},"end":{"line":698,"column":39}},"121":{"start":{"line":700,"column":4},"end":{"line":705,"column":5}},"122":{"start":{"line":704,"column":6},"end":{"line":704,"column":26}},"123":{"start":{"line":707,"column":4},"end":{"line":715,"column":5}},"124":{"start":{"line":708,"column":6},"end":{"line":714,"column":7}},"125":{"start":{"line":713,"column":8},"end":{"line":713,"column":84}},"126":{"start":{"line":716,"column":4},"end":{"line":718,"column":5}},"127":{"start":{"line":717,"column":6},"end":{"line":717,"column":43}},"128":{"start":{"line":720,"column":4},"end":{"line":732,"column":7}},"129":{"start":{"line":721,"column":6},"end":{"line":731,"column":7}},"130":{"start":{"line":722,"column":44},"end":{"line":725,"column":10}},"131":{"start":{"line":723,"column":10},"end":{"line":723,"column":24}},"132":{"start":{"line":724,"column":10},"end":{"line":724,"column":47}},"133":{"start":{"line":726,"column":8},"end":{"line":726,"column":49}},"134":{"start":{"line":729,"column":8},"end":{"line":729,"column":18}},"135":{"start":{"line":730,"column":8},"end":{"line":730,"column":15}},"136":{"start":{"line":736,"column":4},"end":{"line":738,"column":5}},"137":{"start":{"line":737,"column":6},"end":{"line":737,"column":43}},"138":{"start":{"line":739,"column":4},"end":{"line":739,"column":44}},"139":{"start":{"line":743,"column":4},"end":{"line":745,"column":5}},"140":{"start":{"line":744,"column":6},"end":{"line":744,"column":43}},"141":{"start":{"line":746,"column":4},"end":{"line":746,"column":45}},"142":{"start":{"line":750,"column":4},"end":{"line":752,"column":5}},"143":{"start":{"line":751,"column":6},"end":{"line":751,"column":43}},"144":{"start":{"line":753,"column":4},"end":{"line":753,"column":46}},"145":{"start":{"line":757,"column":4},"end":{"line":759,"column":5}},"146":{"start":{"line":758,"column":6},"end":{"line":758,"column":43}},"147":{"start":{"line":761,"column":4},"end":{"line":761,"column":43}},"148":{"start":{"line":765,"column":4},"end":{"line":767,"column":5}},"149":{"start":{"line":766,"column":6},"end":{"line":766,"column":43}},"150":{"start":{"line":768,"column":4},"end":{"line":780,"column":7}},"151":{"start":{"line":769,"column":6},"end":{"line":779,"column":7}},"152":{"start":{"line":770,"column":44},"end":{"line":773,"column":10}},"153":{"start":{"line":771,"column":10},"end":{"line":771,"column":30}},"154":{"start":{"line":772,"column":10},"end":{"line":772,"column":47}},"155":{"start":{"line":774,"column":8},"end":{"line":774,"column":45}},"156":{"start":{"line":777,"column":8},"end":{"line":777,"column":18}},"157":{"start":{"line":778,"column":8},"end":{"line":778,"column":15}},"158":{"start":{"line":784,"column":4},"end":{"line":786,"column":5}},"159":{"start":{"line":785,"column":6},"end":{"line":785,"column":43}},"160":{"start":{"line":787,"column":4},"end":{"line":787,"column":44}},"161":{"start":{"line":790,"column":18},"end":{"line":794,"column":3}},"162":{"start":{"line":791,"column":4},"end":{"line":793,"column":5}},"163":{"start":{"line":792,"column":6},"end":{"line":792,"column":43}},"164":{"start":{"line":796,"column":19},"end":{"line":800,"column":3}},"165":{"start":{"line":797,"column":4},"end":{"line":799,"column":5}},"166":{"start":{"line":798,"column":6},"end":{"line":798,"column":33}},"167":{"start":{"line":802,"column":24},"end":{"line":806,"column":3}},"168":{"start":{"line":803,"column":4},"end":{"line":805,"column":5}},"169":{"start":{"line":804,"column":6},"end":{"line":804,"column":38}},"170":{"start":{"line":807,"column":13},"end":{"line":814,"column":3}},"171":{"start":{"line":808,"column":4},"end":{"line":810,"column":5}},"172":{"start":{"line":809,"column":6},"end":{"line":809,"column":48}},"173":{"start":{"line":811,"column":4},"end":{"line":813,"column":5}},"174":{"start":{"line":812,"column":6},"end":{"line":812,"column":54}},"175":{"start":{"line":815,"column":22},"end":{"line":819,"column":3}},"176":{"start":{"line":816,"column":4},"end":{"line":818,"column":5}},"177":{"start":{"line":817,"column":6},"end":{"line":817,"column":36}},"178":{"start":{"line":821,"column":20},"end":{"line":828,"column":3}},"179":{"start":{"line":822,"column":4},"end":{"line":827,"column":5}},"180":{"start":{"line":823,"column":6},"end":{"line":826,"column":9}},"181":{"start":{"line":830,"column":20},"end":{"line":834,"column":3}},"182":{"start":{"line":831,"column":4},"end":{"line":833,"column":5}},"183":{"start":{"line":832,"column":6},"end":{"line":832,"column":45}},"184":{"start":{"line":836,"column":22},"end":{"line":852,"column":3}},"185":{"start":{"line":836,"column":47},"end":{"line":852,"column":3}},"186":{"start":{"line":837,"column":21},"end":{"line":837,"column":32}},"187":{"start":{"line":838,"column":4},"end":{"line":845,"column":5}},"188":{"start":{"line":844,"column":6},"end":{"line":844,"column":13}},"189":{"start":{"line":847,"column":4},"end":{"line":851,"column":5}},"190":{"start":{"line":848,"column":6},"end":{"line":848,"column":28}},"191":{"start":{"line":849,"column":6},"end":{"line":849,"column":47}},"192":{"start":{"line":850,"column":6},"end":{"line":850,"column":59}},"193":{"start":{"line":854,"column":26},"end":{"line":858,"column":3}},"194":{"start":{"line":855,"column":4},"end":{"line":857,"column":5}},"195":{"start":{"line":856,"column":6},"end":{"line":856,"column":41}},"196":{"start":{"line":860,"column":18},"end":{"line":868,"column":3}},"197":{"start":{"line":861,"column":4},"end":{"line":867,"column":5}},"198":{"start":{"line":862,"column":6},"end":{"line":862,"column":28}},"199":{"start":{"line":863,"column":6},"end":{"line":863,"column":47}},"200":{"start":{"line":865,"column":6},"end":{"line":865,"column":29}},"201":{"start":{"line":866,"column":6},"end":{"line":866,"column":32}},"202":{"start":{"line":871,"column":4},"end":{"line":871,"column":28}},"203":{"start":{"line":880,"column":8},"end":{"line":880,"column":18}},"204":{"start":{"line":882,"column":28},"end":{"line":882,"column":58}},"205":{"start":{"line":883,"column":27},"end":{"line":883,"column":62}},"206":{"start":{"line":884,"column":4},"end":{"line":899,"column":5}},"207":{"start":{"line":886,"column":6},"end":{"line":888,"column":8}},"208":{"start":{"line":889,"column":6},"end":{"line":893,"column":8}},"209":{"start":{"line":894,"column":6},"end":{"line":898,"column":8}},"210":{"start":{"line":901,"column":64},"end":{"line":906,"column":5}},"211":{"start":{"line":908,"column":40},"end":{"line":910,"column":54}},"212":{"start":{"line":911,"column":4},"end":{"line":911,"column":65}},"213":{"start":{"line":918,"column":8},"end":{"line":918,"column":42}},"214":{"start":{"line":919,"column":4},"end":{"line":921,"column":5}},"215":{"start":{"line":920,"column":6},"end":{"line":920,"column":13}},"216":{"start":{"line":923,"column":4},"end":{"line":927,"column":7}},"217":{"start":{"line":934,"column":8},"end":{"line":934,"column":42}},"218":{"start":{"line":935,"column":4},"end":{"line":937,"column":5}},"219":{"start":{"line":936,"column":6},"end":{"line":936,"column":13}},"220":{"start":{"line":939,"column":4},"end":{"line":946,"column":6}},"221":{"start":{"line":949,"column":14},"end":{"line":955,"column":3}},"222":{"start":{"line":950,"column":53},"end":{"line":950,"column":63}},"223":{"start":{"line":951,"column":4},"end":{"line":953,"column":5}},"224":{"start":{"line":952,"column":6},"end":{"line":952,"column":48}},"225":{"start":{"line":954,"column":4},"end":{"line":954,"column":75}},"226":{"start":{"line":958,"column":12},"end":{"line":958,"column":62}},"227":{"start":{"line":958,"column":21},"end":{"line":958,"column":62}},"228":{"start":{"line":960,"column":19},"end":{"line":969,"column":3}},"229":{"start":{"line":961,"column":4},"end":{"line":967,"column":5}},"230":{"start":{"line":962,"column":6},"end":{"line":966,"column":9}},"231":{"start":{"line":968,"column":4},"end":{"line":968,"column":31}},"232":{"start":{"line":972,"column":38},"end":{"line":972,"column":74}},"233":{"start":{"line":974,"column":4},"end":{"line":1010,"column":5}},"234":{"start":{"line":975,"column":6},"end":{"line":1005,"column":8}},"235":{"start":{"line":986,"column":14},"end":{"line":986,"column":36}},"236":{"start":{"line":987,"column":14},"end":{"line":987,"column":55}},"237":{"start":{"line":1006,"column":11},"end":{"line":1010,"column":5}},"238":{"start":{"line":1007,"column":6},"end":{"line":1007,"column":49}},"239":{"start":{"line":1009,"column":6},"end":{"line":1009,"column":42}},"240":{"start":{"line":1014,"column":21},"end":{"line":1014,"column":56}},"241":{"start":{"line":1016,"column":4},"end":{"line":1018,"column":5}},"242":{"start":{"line":1017,"column":6},"end":{"line":1017,"column":44}},"243":{"start":{"line":1020,"column":4},"end":{"line":1022,"column":5}},"244":{"start":{"line":1021,"column":6},"end":{"line":1021,"column":57}},"245":{"start":{"line":1024,"column":4},"end":{"line":1026,"column":5}},"246":{"start":{"line":1025,"column":6},"end":{"line":1025,"column":42}},"247":{"start":{"line":1028,"column":4},"end":{"line":1030,"column":5}},"248":{"start":{"line":1029,"column":6},"end":{"line":1029,"column":43}},"249":{"start":{"line":1032,"column":4},"end":{"line":1034,"column":5}},"250":{"start":{"line":1033,"column":6},"end":{"line":1033,"column":44}},"251":{"start":{"line":1036,"column":4},"end":{"line":1038,"column":5}},"252":{"start":{"line":1037,"column":6},"end":{"line":1037,"column":28}},"253":{"start":{"line":1040,"column":4},"end":{"line":1040,"column":20}},"254":{"start":{"line":1044,"column":4},"end":{"line":1046,"column":5}},"255":{"start":{"line":1045,"column":6},"end":{"line":1045,"column":49}},"256":{"start":{"line":1048,"column":4},"end":{"line":1048,"column":17}},"257":{"start":{"line":1052,"column":25},"end":{"line":1052,"column":41}},"258":{"start":{"line":1055,"column":2},"end":{"line":1057,"column":3}},"259":{"start":{"line":1056,"column":4},"end":{"line":1056,"column":41}},"260":{"start":{"line":1058,"column":2},"end":{"line":1058,"column":34}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":27,"column":27},"end":{"line":27,"column":28}},"loc":{"start":{"line":32,"column":85},"end":{"line":86,"column":1}},"line":32},"1":{"name":"(anonymous_1)","decl":{"start":{"line":399,"column":18},"end":{"line":399,"column":19}},"loc":{"start":{"line":399,"column":37},"end":{"line":405,"column":1}},"line":399},"2":{"name":"(anonymous_2)","decl":{"start":{"line":401,"column":28},"end":{"line":401,"column":29}},"loc":{"start":{"line":401,"column":46},"end":{"line":403,"column":3}},"line":401},"3":{"name":"(anonymous_3)","decl":{"start":{"line":556,"column":2},"end":{"line":556,"column":3}},"loc":{"start":{"line":556,"column":32},"end":{"line":566,"column":3}},"line":556},"4":{"name":"(anonymous_4)","decl":{"start":{"line":568,"column":2},"end":{"line":568,"column":3}},"loc":{"start":{"line":568,"column":51},"end":{"line":615,"column":3}},"line":568},"5":{"name":"(anonymous_5)","decl":{"start":{"line":601,"column":23},"end":{"line":601,"column":24}},"loc":{"start":{"line":601,"column":44},"end":{"line":614,"column":5}},"line":601},"6":{"name":"(anonymous_6)","decl":{"start":{"line":603,"column":84},"end":{"line":603,"column":85}},"loc":{"start":{"line":603,"column":94},"end":{"line":606,"column":9}},"line":603},"7":{"name":"(anonymous_7)","decl":{"start":{"line":617,"column":2},"end":{"line":617,"column":3}},"loc":{"start":{"line":617,"column":34},"end":{"line":625,"column":3}},"line":617},"8":{"name":"(anonymous_8)","decl":{"start":{"line":627,"column":2},"end":{"line":627,"column":3}},"loc":{"start":{"line":627,"column":28},"end":{"line":644,"column":3}},"line":627},"9":{"name":"(anonymous_9)","decl":{"start":{"line":633,"column":25},"end":{"line":633,"column":26}},"loc":{"start":{"line":633,"column":52},"end":{"line":642,"column":7}},"line":633},"10":{"name":"(anonymous_10)","decl":{"start":{"line":646,"column":2},"end":{"line":646,"column":3}},"loc":{"start":{"line":646,"column":41},"end":{"line":654,"column":3}},"line":646},"11":{"name":"(anonymous_11)","decl":{"start":{"line":656,"column":32},"end":{"line":656,"column":33}},"loc":{"start":{"line":656,"column":57},"end":{"line":664,"column":3}},"line":656},"12":{"name":"(anonymous_12)","decl":{"start":{"line":666,"column":29},"end":{"line":666,"column":30}},"loc":{"start":{"line":666,"column":51},"end":{"line":669,"column":3}},"line":666},"13":{"name":"(anonymous_13)","decl":{"start":{"line":671,"column":2},"end":{"line":671,"column":3}},"loc":{"start":{"line":671,"column":48},"end":{"line":733,"column":3}},"line":671},"14":{"name":"(anonymous_14)","decl":{"start":{"line":720,"column":23},"end":{"line":720,"column":24}},"loc":{"start":{"line":720,"column":44},"end":{"line":732,"column":5}},"line":720},"15":{"name":"(anonymous_15)","decl":{"start":{"line":722,"column":98},"end":{"line":722,"column":99}},"loc":{"start":{"line":722,"column":125},"end":{"line":725,"column":9}},"line":722},"16":{"name":"(anonymous_16)","decl":{"start":{"line":735,"column":2},"end":{"line":735,"column":3}},"loc":{"start":{"line":735,"column":18},"end":{"line":740,"column":3}},"line":735},"17":{"name":"(anonymous_17)","decl":{"start":{"line":742,"column":2},"end":{"line":742,"column":3}},"loc":{"start":{"line":742,"column":19},"end":{"line":747,"column":3}},"line":742},"18":{"name":"(anonymous_18)","decl":{"start":{"line":749,"column":2},"end":{"line":749,"column":3}},"loc":{"start":{"line":749,"column":20},"end":{"line":754,"column":3}},"line":749},"19":{"name":"(anonymous_19)","decl":{"start":{"line":756,"column":2},"end":{"line":756,"column":3}},"loc":{"start":{"line":756,"column":17},"end":{"line":762,"column":3}},"line":756},"20":{"name":"(anonymous_20)","decl":{"start":{"line":764,"column":2},"end":{"line":764,"column":3}},"loc":{"start":{"line":764,"column":16},"end":{"line":781,"column":3}},"line":764},"21":{"name":"(anonymous_21)","decl":{"start":{"line":768,"column":23},"end":{"line":768,"column":24}},"loc":{"start":{"line":768,"column":44},"end":{"line":780,"column":5}},"line":768},"22":{"name":"(anonymous_22)","decl":{"start":{"line":770,"column":90},"end":{"line":770,"column":91}},"loc":{"start":{"line":770,"column":107},"end":{"line":773,"column":9}},"line":770},"23":{"name":"(anonymous_23)","decl":{"start":{"line":783,"column":2},"end":{"line":783,"column":3}},"loc":{"start":{"line":783,"column":18},"end":{"line":788,"column":3}},"line":783},"24":{"name":"(anonymous_24)","decl":{"start":{"line":790,"column":18},"end":{"line":790,"column":19}},"loc":{"start":{"line":790,"column":67},"end":{"line":794,"column":3}},"line":790},"25":{"name":"(anonymous_25)","decl":{"start":{"line":796,"column":19},"end":{"line":796,"column":20}},"loc":{"start":{"line":796,"column":25},"end":{"line":800,"column":3}},"line":796},"26":{"name":"(anonymous_26)","decl":{"start":{"line":802,"column":24},"end":{"line":802,"column":25}},"loc":{"start":{"line":802,"column":30},"end":{"line":806,"column":3}},"line":802},"27":{"name":"(anonymous_27)","decl":{"start":{"line":807,"column":13},"end":{"line":807,"column":14}},"loc":{"start":{"line":807,"column":62},"end":{"line":814,"column":3}},"line":807},"28":{"name":"(anonymous_28)","decl":{"start":{"line":815,"column":22},"end":{"line":815,"column":23}},"loc":{"start":{"line":815,"column":28},"end":{"line":819,"column":3}},"line":815},"29":{"name":"(anonymous_29)","decl":{"start":{"line":821,"column":20},"end":{"line":821,"column":21}},"loc":{"start":{"line":821,"column":26},"end":{"line":828,"column":3}},"line":821},"30":{"name":"(anonymous_30)","decl":{"start":{"line":830,"column":20},"end":{"line":830,"column":21}},"loc":{"start":{"line":830,"column":69},"end":{"line":834,"column":3}},"line":830},"31":{"name":"(anonymous_31)","decl":{"start":{"line":836,"column":22},"end":{"line":836,"column":23}},"loc":{"start":{"line":836,"column":47},"end":{"line":852,"column":3}},"line":836},"32":{"name":"(anonymous_32)","decl":{"start":{"line":836,"column":47},"end":{"line":836,"column":48}},"loc":{"start":{"line":836,"column":96},"end":{"line":852,"column":3}},"line":836},"33":{"name":"(anonymous_33)","decl":{"start":{"line":854,"column":26},"end":{"line":854,"column":27}},"loc":{"start":{"line":854,"column":31},"end":{"line":858,"column":3}},"line":854},"34":{"name":"(anonymous_34)","decl":{"start":{"line":860,"column":18},"end":{"line":860,"column":19}},"loc":{"start":{"line":860,"column":36},"end":{"line":868,"column":3}},"line":860},"35":{"name":"(anonymous_35)","decl":{"start":{"line":870,"column":2},"end":{"line":870,"column":3}},"loc":{"start":{"line":870,"column":25},"end":{"line":872,"column":3}},"line":870},"36":{"name":"(anonymous_36)","decl":{"start":{"line":874,"column":2},"end":{"line":874,"column":3}},"loc":{"start":{"line":874,"column":32},"end":{"line":912,"column":3}},"line":874},"37":{"name":"(anonymous_37)","decl":{"start":{"line":914,"column":2},"end":{"line":914,"column":3}},"loc":{"start":{"line":914,"column":37},"end":{"line":928,"column":3}},"line":914},"38":{"name":"(anonymous_38)","decl":{"start":{"line":930,"column":2},"end":{"line":930,"column":3}},"loc":{"start":{"line":930,"column":28},"end":{"line":947,"column":3}},"line":930},"39":{"name":"(anonymous_39)","decl":{"start":{"line":949,"column":14},"end":{"line":949,"column":15}},"loc":{"start":{"line":949,"column":28},"end":{"line":955,"column":3}},"line":949},"40":{"name":"(anonymous_40)","decl":{"start":{"line":958,"column":12},"end":{"line":958,"column":13}},"loc":{"start":{"line":958,"column":21},"end":{"line":958,"column":62}},"line":958},"41":{"name":"(anonymous_41)","decl":{"start":{"line":960,"column":19},"end":{"line":960,"column":20}},"loc":{"start":{"line":960,"column":28},"end":{"line":969,"column":3}},"line":960},"42":{"name":"(anonymous_42)","decl":{"start":{"line":971,"column":2},"end":{"line":971,"column":3}},"loc":{"start":{"line":971,"column":11},"end":{"line":1011,"column":3}},"line":971},"43":{"name":"(anonymous_43)","decl":{"start":{"line":985,"column":17},"end":{"line":985,"column":18}},"loc":{"start":{"line":985,"column":26},"end":{"line":988,"column":13}},"line":985},"44":{"name":"(anonymous_44)","decl":{"start":{"line":1013,"column":2},"end":{"line":1013,"column":3}},"loc":{"start":{"line":1013,"column":57},"end":{"line":1041,"column":3}},"line":1013},"45":{"name":"(anonymous_45)","decl":{"start":{"line":1043,"column":2},"end":{"line":1043,"column":3}},"loc":{"start":{"line":1043,"column":41},"end":{"line":1049,"column":3}},"line":1043},"46":{"name":"hasTorch","decl":{"start":{"line":1054,"column":16},"end":{"line":1054,"column":24}},"loc":{"start":{"line":1054,"column":27},"end":{"line":1059,"column":1}},"line":1054}},"branchMap":{"0":{"loc":{"start":{"line":35,"column":2},"end":{"line":51,"column":3}},"type":"if","locations":[{"start":{"line":35,"column":2},"end":{"line":51,"column":3}},{"start":{"line":37,"column":9},"end":{"line":51,"column":3}}],"line":35},"1":{"loc":{"start":{"line":37,"column":9},"end":{"line":51,"column":3}},"type":"if","locations":[{"start":{"line":37,"column":9},"end":{"line":51,"column":3}},{"start":{"line":47,"column":9},"end":{"line":51,"column":3}}],"line":37},"2":{"loc":{"start":{"line":42,"column":4},"end":{"line":46,"column":5}},"type":"if","locations":[{"start":{"line":42,"column":4},"end":{"line":46,"column":5}},{"start":{"line":44,"column":11},"end":{"line":46,"column":5}}],"line":42},"3":{"loc":{"start":{"line":47,"column":9},"end":{"line":51,"column":3}},"type":"if","locations":[{"start":{"line":47,"column":9},"end":{"line":51,"column":3}},{"start":{"line":49,"column":9},"end":{"line":51,"column":3}}],"line":47},"4":{"loc":{"start":{"line":49,"column":9},"end":{"line":51,"column":3}},"type":"if","locations":[{"start":{"line":49,"column":9},"end":{"line":51,"column":3}},{"start":{},"end":{}}],"line":49},"5":{"loc":{"start":{"line":53,"column":2},"end":{"line":80,"column":3}},"type":"if","locations":[{"start":{"line":53,"column":2},"end":{"line":80,"column":3}},{"start":{},"end":{}}],"line":53},"6":{"loc":{"start":{"line":54,"column":4},"end":{"line":79,"column":5}},"type":"if","locations":[{"start":{"line":54,"column":4},"end":{"line":79,"column":5}},{"start":{"line":56,"column":11},"end":{"line":79,"column":5}}],"line":54},"7":{"loc":{"start":{"line":56,"column":11},"end":{"line":79,"column":5}},"type":"if","locations":[{"start":{"line":56,"column":11},"end":{"line":79,"column":5}},{"start":{"line":75,"column":11},"end":{"line":79,"column":5}}],"line":56},"8":{"loc":{"start":{"line":57,"column":6},"end":{"line":74,"column":7}},"type":"if","locations":[{"start":{"line":57,"column":6},"end":{"line":74,"column":7}},{"start":{"line":67,"column":13},"end":{"line":74,"column":7}}],"line":57},"9":{"loc":{"start":{"line":62,"column":8},"end":{"line":66,"column":9}},"type":"if","locations":[{"start":{"line":62,"column":8},"end":{"line":66,"column":9}},{"start":{"line":64,"column":15},"end":{"line":66,"column":9}}],"line":62},"10":{"loc":{"start":{"line":67,"column":13},"end":{"line":74,"column":7}},"type":"if","locations":[{"start":{"line":67,"column":13},"end":{"line":74,"column":7}},{"start":{},"end":{}}],"line":67},"11":{"loc":{"start":{"line":75,"column":11},"end":{"line":79,"column":5}},"type":"if","locations":[{"start":{"line":75,"column":11},"end":{"line":79,"column":5}},{"start":{"line":77,"column":11},"end":{"line":79,"column":5}}],"line":75},"12":{"loc":{"start":{"line":77,"column":11},"end":{"line":79,"column":5}},"type":"if","locations":[{"start":{"line":77,"column":11},"end":{"line":79,"column":5}},{"start":{},"end":{}}],"line":77},"13":{"loc":{"start":{"line":439,"column":24},"end":{"line":439,"column":57}},"type":"binary-expr","locations":[{"start":{"line":439,"column":24},"end":{"line":439,"column":51}},{"start":{"line":439,"column":55},"end":{"line":439,"column":57}}],"line":439},"14":{"loc":{"start":{"line":440,"column":29},"end":{"line":440,"column":62}},"type":"binary-expr","locations":[{"start":{"line":440,"column":29},"end":{"line":440,"column":56}},{"start":{"line":440,"column":60},"end":{"line":440,"column":62}}],"line":440},"15":{"loc":{"start":{"line":441,"column":35},"end":{"line":441,"column":68}},"type":"binary-expr","locations":[{"start":{"line":441,"column":35},"end":{"line":441,"column":62}},{"start":{"line":441,"column":66},"end":{"line":441,"column":68}}],"line":441},"16":{"loc":{"start":{"line":442,"column":30},"end":{"line":442,"column":78}},"type":"binary-expr","locations":[{"start":{"line":442,"column":30},"end":{"line":442,"column":72}},{"start":{"line":442,"column":76},"end":{"line":442,"column":78}}],"line":442},"17":{"loc":{"start":{"line":443,"column":30},"end":{"line":443,"column":78}},"type":"binary-expr","locations":[{"start":{"line":443,"column":30},"end":{"line":443,"column":72}},{"start":{"line":443,"column":76},"end":{"line":443,"column":78}}],"line":443},"18":{"loc":{"start":{"line":444,"column":28},"end":{"line":444,"column":66}},"type":"binary-expr","locations":[{"start":{"line":444,"column":28},"end":{"line":444,"column":60}},{"start":{"line":444,"column":64},"end":{"line":444,"column":66}}],"line":444},"19":{"loc":{"start":{"line":518,"column":24},"end":{"line":518,"column":57}},"type":"binary-expr","locations":[{"start":{"line":518,"column":24},"end":{"line":518,"column":51}},{"start":{"line":518,"column":55},"end":{"line":518,"column":57}}],"line":518},"20":{"loc":{"start":{"line":520,"column":30},"end":{"line":520,"column":98}},"type":"binary-expr","locations":[{"start":{"line":520,"column":30},"end":{"line":520,"column":92}},{"start":{"line":520,"column":96},"end":{"line":520,"column":98}}],"line":520},"21":{"loc":{"start":{"line":520,"column":31},"end":{"line":520,"column":79}},"type":"binary-expr","locations":[{"start":{"line":520,"column":31},"end":{"line":520,"column":73}},{"start":{"line":520,"column":77},"end":{"line":520,"column":79}}],"line":520},"22":{"loc":{"start":{"line":522,"column":30},"end":{"line":522,"column":98}},"type":"binary-expr","locations":[{"start":{"line":522,"column":30},"end":{"line":522,"column":92}},{"start":{"line":522,"column":96},"end":{"line":522,"column":98}}],"line":522},"23":{"loc":{"start":{"line":522,"column":31},"end":{"line":522,"column":79}},"type":"binary-expr","locations":[{"start":{"line":522,"column":31},"end":{"line":522,"column":73}},{"start":{"line":522,"column":77},"end":{"line":522,"column":79}}],"line":522},"24":{"loc":{"start":{"line":524,"column":29},"end":{"line":524,"column":80}},"type":"binary-expr","locations":[{"start":{"line":524,"column":29},"end":{"line":524,"column":74}},{"start":{"line":524,"column":78},"end":{"line":524,"column":80}}],"line":524},"25":{"loc":{"start":{"line":524,"column":30},"end":{"line":524,"column":63}},"type":"binary-expr","locations":[{"start":{"line":524,"column":30},"end":{"line":524,"column":57}},{"start":{"line":524,"column":61},"end":{"line":524,"column":63}}],"line":524},"26":{"loc":{"start":{"line":525,"column":35},"end":{"line":525,"column":92}},"type":"binary-expr","locations":[{"start":{"line":525,"column":35},"end":{"line":525,"column":86}},{"start":{"line":525,"column":90},"end":{"line":525,"column":92}}],"line":525},"27":{"loc":{"start":{"line":525,"column":36},"end":{"line":525,"column":69}},"type":"binary-expr","locations":[{"start":{"line":525,"column":36},"end":{"line":525,"column":63}},{"start":{"line":525,"column":67},"end":{"line":525,"column":69}}],"line":525},"28":{"loc":{"start":{"line":569,"column":4},"end":{"line":571,"column":5}},"type":"if","locations":[{"start":{"line":569,"column":4},"end":{"line":571,"column":5}},{"start":{},"end":{}}],"line":569},"29":{"loc":{"start":{"line":572,"column":4},"end":{"line":574,"column":5}},"type":"if","locations":[{"start":{"line":572,"column":4},"end":{"line":574,"column":5}},{"start":{},"end":{}}],"line":572},"30":{"loc":{"start":{"line":576,"column":4},"end":{"line":587,"column":5}},"type":"if","locations":[{"start":{"line":576,"column":4},"end":{"line":587,"column":5}},{"start":{},"end":{}}],"line":576},"31":{"loc":{"start":{"line":577,"column":6},"end":{"line":586,"column":7}},"type":"if","locations":[{"start":{"line":577,"column":6},"end":{"line":586,"column":7}},{"start":{},"end":{}}],"line":577},"32":{"loc":{"start":{"line":580,"column":8},"end":{"line":585,"column":9}},"type":"if","locations":[{"start":{"line":580,"column":8},"end":{"line":585,"column":9}},{"start":{},"end":{}}],"line":580},"33":{"loc":{"start":{"line":581,"column":10},"end":{"line":584,"column":11}},"type":"if","locations":[{"start":{"line":581,"column":10},"end":{"line":584,"column":11}},{"start":{},"end":{}}],"line":581},"34":{"loc":{"start":{"line":589,"column":4},"end":{"line":591,"column":5}},"type":"if","locations":[{"start":{"line":589,"column":4},"end":{"line":591,"column":5}},{"start":{},"end":{}}],"line":589},"35":{"loc":{"start":{"line":593,"column":4},"end":{"line":595,"column":5}},"type":"if","locations":[{"start":{"line":593,"column":4},"end":{"line":595,"column":5}},{"start":{},"end":{}}],"line":593},"36":{"loc":{"start":{"line":597,"column":4},"end":{"line":599,"column":5}},"type":"if","locations":[{"start":{"line":597,"column":4},"end":{"line":599,"column":5}},{"start":{},"end":{}}],"line":597},"37":{"loc":{"start":{"line":618,"column":4},"end":{"line":624,"column":5}},"type":"if","locations":[{"start":{"line":618,"column":4},"end":{"line":624,"column":5}},{"start":{"line":620,"column":11},"end":{"line":624,"column":5}}],"line":618},"38":{"loc":{"start":{"line":620,"column":11},"end":{"line":624,"column":5}},"type":"if","locations":[{"start":{"line":620,"column":11},"end":{"line":624,"column":5}},{"start":{"line":622,"column":11},"end":{"line":624,"column":5}}],"line":620},"39":{"loc":{"start":{"line":628,"column":4},"end":{"line":643,"column":5}},"type":"if","locations":[{"start":{"line":628,"column":4},"end":{"line":643,"column":5}},{"start":{"line":630,"column":11},"end":{"line":643,"column":5}}],"line":628},"40":{"loc":{"start":{"line":630,"column":11},"end":{"line":643,"column":5}},"type":"if","locations":[{"start":{"line":630,"column":11},"end":{"line":643,"column":5}},{"start":{"line":632,"column":11},"end":{"line":643,"column":5}}],"line":630},"41":{"loc":{"start":{"line":632,"column":11},"end":{"line":643,"column":5}},"type":"if","locations":[{"start":{"line":632,"column":11},"end":{"line":643,"column":5}},{"start":{},"end":{}}],"line":632},"42":{"loc":{"start":{"line":647,"column":4},"end":{"line":653,"column":5}},"type":"if","locations":[{"start":{"line":647,"column":4},"end":{"line":653,"column":5}},{"start":{"line":649,"column":11},"end":{"line":653,"column":5}}],"line":647},"43":{"loc":{"start":{"line":649,"column":11},"end":{"line":653,"column":5}},"type":"if","locations":[{"start":{"line":649,"column":11},"end":{"line":653,"column":5}},{"start":{"line":651,"column":11},"end":{"line":653,"column":5}}],"line":649},"44":{"loc":{"start":{"line":657,"column":4},"end":{"line":663,"column":5}},"type":"if","locations":[{"start":{"line":657,"column":4},"end":{"line":663,"column":5}},{"start":{"line":659,"column":11},"end":{"line":663,"column":5}}],"line":657},"45":{"loc":{"start":{"line":659,"column":11},"end":{"line":663,"column":5}},"type":"if","locations":[{"start":{"line":659,"column":11},"end":{"line":663,"column":5}},{"start":{"line":661,"column":11},"end":{"line":663,"column":5}}],"line":659},"46":{"loc":{"start":{"line":672,"column":4},"end":{"line":676,"column":5}},"type":"if","locations":[{"start":{"line":672,"column":4},"end":{"line":676,"column":5}},{"start":{"line":674,"column":11},"end":{"line":676,"column":5}}],"line":672},"47":{"loc":{"start":{"line":672,"column":8},"end":{"line":672,"column":47}},"type":"binary-expr","locations":[{"start":{"line":672,"column":8},"end":{"line":672,"column":16}},{"start":{"line":672,"column":20},"end":{"line":672,"column":47}}],"line":672},"48":{"loc":{"start":{"line":674,"column":11},"end":{"line":676,"column":5}},"type":"if","locations":[{"start":{"line":674,"column":11},"end":{"line":676,"column":5}},{"start":{},"end":{}}],"line":674},"49":{"loc":{"start":{"line":677,"column":4},"end":{"line":688,"column":5}},"type":"if","locations":[{"start":{"line":677,"column":4},"end":{"line":688,"column":5}},{"start":{},"end":{}}],"line":677},"50":{"loc":{"start":{"line":678,"column":6},"end":{"line":687,"column":7}},"type":"if","locations":[{"start":{"line":678,"column":6},"end":{"line":687,"column":7}},{"start":{},"end":{}}],"line":678},"51":{"loc":{"start":{"line":681,"column":8},"end":{"line":686,"column":9}},"type":"if","locations":[{"start":{"line":681,"column":8},"end":{"line":686,"column":9}},{"start":{},"end":{}}],"line":681},"52":{"loc":{"start":{"line":682,"column":10},"end":{"line":685,"column":11}},"type":"if","locations":[{"start":{"line":682,"column":10},"end":{"line":685,"column":11}},{"start":{},"end":{}}],"line":682},"53":{"loc":{"start":{"line":690,"column":4},"end":{"line":695,"column":5}},"type":"if","locations":[{"start":{"line":690,"column":4},"end":{"line":695,"column":5}},{"start":{},"end":{}}],"line":690},"54":{"loc":{"start":{"line":691,"column":6},"end":{"line":694,"column":7}},"type":"if","locations":[{"start":{"line":691,"column":6},"end":{"line":694,"column":7}},{"start":{},"end":{}}],"line":691},"55":{"loc":{"start":{"line":691,"column":10},"end":{"line":691,"column":74}},"type":"binary-expr","locations":[{"start":{"line":691,"column":10},"end":{"line":691,"column":30}},{"start":{"line":691,"column":34},"end":{"line":691,"column":74}}],"line":691},"56":{"loc":{"start":{"line":700,"column":4},"end":{"line":705,"column":5}},"type":"if","locations":[{"start":{"line":700,"column":4},"end":{"line":705,"column":5}},{"start":{},"end":{}}],"line":700},"57":{"loc":{"start":{"line":701,"column":6},"end":{"line":702,"column":80}},"type":"binary-expr","locations":[{"start":{"line":701,"column":6},"end":{"line":701,"column":19}},{"start":{"line":702,"column":6},"end":{"line":702,"column":80}}],"line":701},"58":{"loc":{"start":{"line":707,"column":4},"end":{"line":715,"column":5}},"type":"if","locations":[{"start":{"line":707,"column":4},"end":{"line":715,"column":5}},{"start":{},"end":{}}],"line":707},"59":{"loc":{"start":{"line":708,"column":6},"end":{"line":714,"column":7}},"type":"if","locations":[{"start":{"line":708,"column":6},"end":{"line":714,"column":7}},{"start":{},"end":{}}],"line":708},"60":{"loc":{"start":{"line":709,"column":8},"end":{"line":710,"column":82}},"type":"binary-expr","locations":[{"start":{"line":709,"column":9},"end":{"line":709,"column":22}},{"start":{"line":709,"column":26},"end":{"line":709,"column":38}},{"start":{"line":710,"column":8},"end":{"line":710,"column":82}}],"line":709},"61":{"loc":{"start":{"line":716,"column":4},"end":{"line":718,"column":5}},"type":"if","locations":[{"start":{"line":716,"column":4},"end":{"line":718,"column":5}},{"start":{},"end":{}}],"line":716},"62":{"loc":{"start":{"line":736,"column":4},"end":{"line":738,"column":5}},"type":"if","locations":[{"start":{"line":736,"column":4},"end":{"line":738,"column":5}},{"start":{},"end":{}}],"line":736},"63":{"loc":{"start":{"line":743,"column":4},"end":{"line":745,"column":5}},"type":"if","locations":[{"start":{"line":743,"column":4},"end":{"line":745,"column":5}},{"start":{},"end":{}}],"line":743},"64":{"loc":{"start":{"line":750,"column":4},"end":{"line":752,"column":5}},"type":"if","locations":[{"start":{"line":750,"column":4},"end":{"line":752,"column":5}},{"start":{},"end":{}}],"line":750},"65":{"loc":{"start":{"line":757,"column":4},"end":{"line":759,"column":5}},"type":"if","locations":[{"start":{"line":757,"column":4},"end":{"line":759,"column":5}},{"start":{},"end":{}}],"line":757},"66":{"loc":{"start":{"line":765,"column":4},"end":{"line":767,"column":5}},"type":"if","locations":[{"start":{"line":765,"column":4},"end":{"line":767,"column":5}},{"start":{},"end":{}}],"line":765},"67":{"loc":{"start":{"line":784,"column":4},"end":{"line":786,"column":5}},"type":"if","locations":[{"start":{"line":784,"column":4},"end":{"line":786,"column":5}},{"start":{},"end":{}}],"line":784},"68":{"loc":{"start":{"line":791,"column":4},"end":{"line":793,"column":5}},"type":"if","locations":[{"start":{"line":791,"column":4},"end":{"line":793,"column":5}},{"start":{},"end":{}}],"line":791},"69":{"loc":{"start":{"line":797,"column":4},"end":{"line":799,"column":5}},"type":"if","locations":[{"start":{"line":797,"column":4},"end":{"line":799,"column":5}},{"start":{},"end":{}}],"line":797},"70":{"loc":{"start":{"line":803,"column":4},"end":{"line":805,"column":5}},"type":"if","locations":[{"start":{"line":803,"column":4},"end":{"line":805,"column":5}},{"start":{},"end":{}}],"line":803},"71":{"loc":{"start":{"line":808,"column":4},"end":{"line":810,"column":5}},"type":"if","locations":[{"start":{"line":808,"column":4},"end":{"line":810,"column":5}},{"start":{},"end":{}}],"line":808},"72":{"loc":{"start":{"line":808,"column":8},"end":{"line":808,"column":52}},"type":"binary-expr","locations":[{"start":{"line":808,"column":8},"end":{"line":808,"column":24}},{"start":{"line":808,"column":28},"end":{"line":808,"column":52}}],"line":808},"73":{"loc":{"start":{"line":811,"column":4},"end":{"line":813,"column":5}},"type":"if","locations":[{"start":{"line":811,"column":4},"end":{"line":813,"column":5}},{"start":{},"end":{}}],"line":811},"74":{"loc":{"start":{"line":811,"column":8},"end":{"line":811,"column":57}},"type":"binary-expr","locations":[{"start":{"line":811,"column":8},"end":{"line":811,"column":30}},{"start":{"line":811,"column":34},"end":{"line":811,"column":57}}],"line":811},"75":{"loc":{"start":{"line":816,"column":4},"end":{"line":818,"column":5}},"type":"if","locations":[{"start":{"line":816,"column":4},"end":{"line":818,"column":5}},{"start":{},"end":{}}],"line":816},"76":{"loc":{"start":{"line":822,"column":4},"end":{"line":827,"column":5}},"type":"if","locations":[{"start":{"line":822,"column":4},"end":{"line":827,"column":5}},{"start":{},"end":{}}],"line":822},"77":{"loc":{"start":{"line":831,"column":4},"end":{"line":833,"column":5}},"type":"if","locations":[{"start":{"line":831,"column":4},"end":{"line":833,"column":5}},{"start":{},"end":{}}],"line":831},"78":{"loc":{"start":{"line":838,"column":4},"end":{"line":845,"column":5}},"type":"if","locations":[{"start":{"line":838,"column":4},"end":{"line":845,"column":5}},{"start":{},"end":{}}],"line":838},"79":{"loc":{"start":{"line":839,"column":6},"end":{"line":842,"column":64}},"type":"binary-expr","locations":[{"start":{"line":839,"column":6},"end":{"line":839,"column":28}},{"start":{"line":840,"column":6},"end":{"line":840,"column":33}},{"start":{"line":841,"column":6},"end":{"line":841,"column":60}},{"start":{"line":842,"column":6},"end":{"line":842,"column":64}}],"line":839},"80":{"loc":{"start":{"line":847,"column":4},"end":{"line":851,"column":5}},"type":"if","locations":[{"start":{"line":847,"column":4},"end":{"line":851,"column":5}},{"start":{},"end":{}}],"line":847},"81":{"loc":{"start":{"line":855,"column":4},"end":{"line":857,"column":5}},"type":"if","locations":[{"start":{"line":855,"column":4},"end":{"line":857,"column":5}},{"start":{},"end":{}}],"line":855},"82":{"loc":{"start":{"line":861,"column":4},"end":{"line":867,"column":5}},"type":"if","locations":[{"start":{"line":861,"column":4},"end":{"line":867,"column":5}},{"start":{"line":864,"column":11},"end":{"line":867,"column":5}}],"line":861},"83":{"loc":{"start":{"line":884,"column":4},"end":{"line":899,"column":5}},"type":"if","locations":[{"start":{"line":884,"column":4},"end":{"line":899,"column":5}},{"start":{},"end":{}}],"line":884},"84":{"loc":{"start":{"line":884,"column":8},"end":{"line":884,"column":56}},"type":"binary-expr","locations":[{"start":{"line":884,"column":8},"end":{"line":884,"column":29}},{"start":{"line":884,"column":33},"end":{"line":884,"column":56}}],"line":884},"85":{"loc":{"start":{"line":908,"column":40},"end":{"line":910,"column":54}},"type":"cond-expr","locations":[{"start":{"line":909,"column":8},"end":{"line":909,"column":50}},{"start":{"line":910,"column":8},"end":{"line":910,"column":54}}],"line":908},"86":{"loc":{"start":{"line":919,"column":4},"end":{"line":921,"column":5}},"type":"if","locations":[{"start":{"line":919,"column":4},"end":{"line":921,"column":5}},{"start":{},"end":{}}],"line":919},"87":{"loc":{"start":{"line":935,"column":4},"end":{"line":937,"column":5}},"type":"if","locations":[{"start":{"line":935,"column":4},"end":{"line":937,"column":5}},{"start":{},"end":{}}],"line":935},"88":{"loc":{"start":{"line":951,"column":4},"end":{"line":953,"column":5}},"type":"if","locations":[{"start":{"line":951,"column":4},"end":{"line":953,"column":5}},{"start":{},"end":{}}],"line":951},"89":{"loc":{"start":{"line":954,"column":11},"end":{"line":954,"column":74}},"type":"cond-expr","locations":[{"start":{"line":954,"column":26},"end":{"line":954,"column":44}},{"start":{"line":954,"column":47},"end":{"line":954,"column":74}}],"line":954},"90":{"loc":{"start":{"line":961,"column":4},"end":{"line":967,"column":5}},"type":"if","locations":[{"start":{"line":961,"column":4},"end":{"line":967,"column":5}},{"start":{},"end":{}}],"line":961},"91":{"loc":{"start":{"line":974,"column":4},"end":{"line":1010,"column":5}},"type":"if","locations":[{"start":{"line":974,"column":4},"end":{"line":1010,"column":5}},{"start":{"line":1006,"column":11},"end":{"line":1010,"column":5}}],"line":974},"92":{"loc":{"start":{"line":974,"column":8},"end":{"line":974,"column":49}},"type":"binary-expr","locations":[{"start":{"line":974,"column":8},"end":{"line":974,"column":31}},{"start":{"line":974,"column":35},"end":{"line":974,"column":49}}],"line":974},"93":{"loc":{"start":{"line":1006,"column":11},"end":{"line":1010,"column":5}},"type":"if","locations":[{"start":{"line":1006,"column":11},"end":{"line":1010,"column":5}},{"start":{"line":1008,"column":11},"end":{"line":1010,"column":5}}],"line":1006},"94":{"loc":{"start":{"line":1016,"column":4},"end":{"line":1018,"column":5}},"type":"if","locations":[{"start":{"line":1016,"column":4},"end":{"line":1018,"column":5}},{"start":{},"end":{}}],"line":1016},"95":{"loc":{"start":{"line":1020,"column":4},"end":{"line":1022,"column":5}},"type":"if","locations":[{"start":{"line":1020,"column":4},"end":{"line":1022,"column":5}},{"start":{},"end":{}}],"line":1020},"96":{"loc":{"start":{"line":1024,"column":4},"end":{"line":1026,"column":5}},"type":"if","locations":[{"start":{"line":1024,"column":4},"end":{"line":1026,"column":5}},{"start":{},"end":{}}],"line":1024},"97":{"loc":{"start":{"line":1028,"column":4},"end":{"line":1030,"column":5}},"type":"if","locations":[{"start":{"line":1028,"column":4},"end":{"line":1030,"column":5}},{"start":{},"end":{}}],"line":1028},"98":{"loc":{"start":{"line":1028,"column":8},"end":{"line":1028,"column":40}},"type":"binary-expr","locations":[{"start":{"line":1028,"column":8},"end":{"line":1028,"column":19}},{"start":{"line":1028,"column":23},"end":{"line":1028,"column":40}}],"line":1028},"99":{"loc":{"start":{"line":1032,"column":4},"end":{"line":1034,"column":5}},"type":"if","locations":[{"start":{"line":1032,"column":4},"end":{"line":1034,"column":5}},{"start":{},"end":{}}],"line":1032},"100":{"loc":{"start":{"line":1036,"column":4},"end":{"line":1038,"column":5}},"type":"if","locations":[{"start":{"line":1036,"column":4},"end":{"line":1038,"column":5}},{"start":{},"end":{}}],"line":1036},"101":{"loc":{"start":{"line":1044,"column":4},"end":{"line":1046,"column":5}},"type":"if","locations":[{"start":{"line":1044,"column":4},"end":{"line":1046,"column":5}},{"start":{},"end":{}}],"line":1044},"102":{"loc":{"start":{"line":1044,"column":8},"end":{"line":1044,"column":65}},"type":"binary-expr","locations":[{"start":{"line":1044,"column":8},"end":{"line":1044,"column":33}},{"start":{"line":1044,"column":37},"end":{"line":1044,"column":65}}],"line":1044},"103":{"loc":{"start":{"line":1055,"column":2},"end":{"line":1057,"column":3}},"type":"if","locations":[{"start":{"line":1055,"column":2},"end":{"line":1057,"column":3}},{"start":{},"end":{}}],"line":1055}},"s":{"0":4,"1":4,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":4,"32":4,"33":4,"34":4,"35":4,"36":4,"37":0,"38":0,"39":0,"40":0,"41":4,"42":4,"43":4,"44":4,"45":12,"46":12,"47":12,"48":12,"49":12,"50":12,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":2,"77":0,"78":2,"79":1,"80":1,"81":1,"82":0,"83":1,"84":0,"85":1,"86":1,"87":1,"88":1,"89":1,"90":0,"91":0,"92":2,"93":0,"94":2,"95":1,"96":1,"97":12,"98":2,"99":0,"100":2,"101":1,"102":1,"103":12,"104":1,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"138":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"154":0,"155":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":12,"162":0,"163":0,"164":12,"165":0,"166":0,"167":12,"168":0,"169":0,"170":12,"171":3,"172":1,"173":3,"174":1,"175":12,"176":0,"177":0,"178":12,"179":0,"180":0,"181":12,"182":0,"183":0,"184":12,"185":3,"186":5,"187":5,"188":1,"189":4,"190":3,"191":3,"192":3,"193":12,"194":0,"195":0,"196":12,"197":0,"198":0,"199":0,"200":0,"201":0,"202":0,"203":0,"204":0,"205":0,"206":0,"207":0,"208":0,"209":0,"210":0,"211":0,"212":0,"213":0,"214":0,"215":0,"216":0,"217":0,"218":0,"219":0,"220":0,"221":12,"222":0,"223":0,"224":0,"225":0,"226":12,"227":0,"228":12,"229":0,"230":0,"231":0,"232":0,"233":0,"234":0,"235":0,"236":0,"237":0,"238":0,"239":0,"240":0,"241":0,"242":0,"243":0,"244":0,"245":0,"246":0,"247":0,"248":0,"249":0,"250":0,"251":0,"252":0,"253":0,"254":0,"255":0,"256":0,"257":4,"258":2,"259":2,"260":0},"f":{"0":0,"1":0,"2":0,"3":12,"4":0,"5":0,"6":0,"7":2,"8":1,"9":1,"10":2,"11":2,"12":1,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":3,"28":0,"29":0,"30":0,"31":3,"32":5,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":2},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[4,0],"14":[4,0],"15":[4,0],"16":[4,0],"17":[4,0],"18":[4,0],"19":[4,0],"20":[4,4],"21":[4,0],"22":[4,4],"23":[4,0],"24":[4,0],"25":[4,0],"26":[4,0],"27":[4,0],"28":[0,0],"29":[0,0],"30":[0,0],"31":[0,0],"32":[0,0],"33":[0,0],"34":[0,0],"35":[0,0],"36":[0,0],"37":[0,2],"38":[1,1],"39":[0,1],"40":[0,1],"41":[1,0],"42":[0,2],"43":[1,1],"44":[0,2],"45":[1,1],"46":[0,0],"47":[0,0],"48":[0,0],"49":[0,0],"50":[0,0],"51":[0,0],"52":[0,0],"53":[0,0],"54":[0,0],"55":[0,0],"56":[0,0],"57":[0,0],"58":[0,0],"59":[0,0],"60":[0,0,0],"61":[0,0],"62":[0,0],"63":[0,0],"64":[0,0],"65":[0,0],"66":[0,0],"67":[0,0],"68":[0,0],"69":[0,0],"70":[0,0],"71":[1,2],"72":[3,2],"73":[1,2],"74":[3,2],"75":[0,0],"76":[0,0],"77":[0,0],"78":[1,4],"79":[5,2,2,1],"80":[3,1],"81":[0,0],"82":[0,0],"83":[0,0],"84":[0,0],"85":[0,0],"86":[0,0],"87":[0,0],"88":[0,0],"89":[0,0],"90":[0,0],"91":[0,0],"92":[0,0],"93":[0,0],"94":[0,0],"95":[0,0],"96":[0,0],"97":[0,0],"98":[0,0],"99":[0,0],"100":[0,0],"101":[0,0],"102":[0,0],"103":[2,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"df1e21405b439fdcf2c3dda1d902a02883b2bb38"} +,"D:\\work_file\\tpc\\react-native-camera\\react-native-camera-sig\\src\\index.js": {"path":"D:\\work_file\\tpc\\react-native-camera\\react-native-camera-sig\\src\\index.js","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"9606ceff87ebb292d629f8d97d4d3927bf883749"} +} diff --git a/jest/coverage/lcov-report/FaceDetector.js.html b/jest/coverage/lcov-report/FaceDetector.js.html new file mode 100644 index 000000000..a51fe64f8 --- /dev/null +++ b/jest/coverage/lcov-report/FaceDetector.js.html @@ -0,0 +1,265 @@ + + + + + + Code coverage report for FaceDetector.js + + + + + + + + + +
+
+

All files FaceDetector.js

+
+ +
+ 71.42% + Statements + 5/7 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 33.33% + Functions + 1/3 +
+ + +
+ 83.33% + Lines + 5/6 +
+ + +
+

+ 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;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/jest/coverage/lcov-report/RNCamera.js.html b/jest/coverage/lcov-report/RNCamera.js.html new file mode 100644 index 000000000..d5536ab71 --- /dev/null +++ b/jest/coverage/lcov-report/RNCamera.js.html @@ -0,0 +1,3262 @@ + + + + + + Code coverage report for RNCamera.js + + + + + + + + + +
+
+

All files RNCamera.js

+
+ +
+ 26.05% + Statements + 68/261 +
+ + +
+ 21.8% + Branches + 46/211 +
+ + +
+ 23.4% + Functions + 11/47 +
+ + +
+ 25.86% + Lines + 67/259 +
+ + +
+

+ 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();
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/jest/coverage/lcov-report/base.css b/jest/coverage/lcov-report/base.css new file mode 100644 index 000000000..f418035b4 --- /dev/null +++ b/jest/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/jest/coverage/lcov-report/block-navigation.js b/jest/coverage/lcov-report/block-navigation.js new file mode 100644 index 000000000..530d1ed2b --- /dev/null +++ b/jest/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selector that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/jest/coverage/lcov-report/favicon.png b/jest/coverage/lcov-report/favicon.png new file mode 100644 index 000000000..c1525b811 Binary files /dev/null and b/jest/coverage/lcov-report/favicon.png differ diff --git a/jest/coverage/lcov-report/index.html b/jest/coverage/lcov-report/index.html new file mode 100644 index 000000000..10f987087 --- /dev/null +++ b/jest/coverage/lcov-report/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 27.23% + Statements + 73/268 +
+ + +
+ 22.06% + Branches + 47/213 +
+ + +
+ 24% + Functions + 12/50 +
+ + +
+ 27.16% + Lines + 72/265 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
FaceDetector.js +
+
71.42%5/750%1/233.33%1/383.33%5/6
RNCamera.js +
+
26.05%68/26121.8%46/21123.4%11/4725.86%67/259
index.js +
+
0%0/00%0/00%0/00%0/0
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/jest/coverage/lcov-report/index.js.html b/jest/coverage/lcov-report/index.js.html new file mode 100644 index 000000000..cd713dcbe --- /dev/null +++ b/jest/coverage/lcov-report/index.js.html @@ -0,0 +1,103 @@ + + + + + + Code coverage report for index.js + + + + + + + + + +
+
+

All files index.js

+
+ +
+ 0% + Statements + 0/0 +
+ + +
+ 0% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/0 +
+ + +
+ 0% + Lines + 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 };
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/jest/coverage/lcov-report/prettify.css b/jest/coverage/lcov-report/prettify.css new file mode 100644 index 000000000..b317a7cda --- /dev/null +++ b/jest/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/jest/coverage/lcov-report/prettify.js b/jest/coverage/lcov-report/prettify.js new file mode 100644 index 000000000..b3225238f --- /dev/null +++ b/jest/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/jest/coverage/lcov-report/sort-arrow-sprite.png b/jest/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 000000000..6ed68316e Binary files /dev/null and b/jest/coverage/lcov-report/sort-arrow-sprite.png differ diff --git a/jest/coverage/lcov-report/sorter.js b/jest/coverage/lcov-report/sorter.js new file mode 100644 index 000000000..4ed70ae5a --- /dev/null +++ b/jest/coverage/lcov-report/sorter.js @@ -0,0 +1,210 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + + // Try to create a RegExp from the searchValue. If it fails (invalid regex), + // it will be treated as a plain text search + let searchRegex; + try { + searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive + } catch (error) { + searchRegex = null; + } + + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + let isMatch = false; + + if (searchRegex) { + // If a valid regex was created, use it for matching + isMatch = searchRegex.test(row.textContent); + } else { + // Otherwise, fall back to the original plain text search + isMatch = row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()); + } + + row.style.display = isMatch ? '' : 'none'; + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/jest/coverage/lcov.info b/jest/coverage/lcov.info new file mode 100644 index 000000000..2aaef8bfc --- /dev/null +++ b/jest/coverage/lcov.info @@ -0,0 +1,605 @@ +TN: +SF:src\FaceDetector.js +FN:11,(anonymous_0) +FN:11,(anonymous_1) +FN:55,(anonymous_2) +FNF:3 +FNH:1 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:1,(anonymous_2) +DA:4,1 +DA:6,1 +DA:11,0 +DA:49,1 +DA:56,1 +DA:60,1 +LF:6 +LH:5 +BRDA:6,0,0,1 +BRDA:6,0,1,0 +BRF:2 +BRH:1 +end_of_record +TN: +SF:src\RNCamera.js +FN:27,(anonymous_0) +FN:399,(anonymous_1) +FN:401,(anonymous_2) +FN:556,(anonymous_3) +FN:568,(anonymous_4) +FN:601,(anonymous_5) +FN:603,(anonymous_6) +FN:617,(anonymous_7) +FN:627,(anonymous_8) +FN:633,(anonymous_9) +FN:646,(anonymous_10) +FN:656,(anonymous_11) +FN:666,(anonymous_12) +FN:671,(anonymous_13) +FN:720,(anonymous_14) +FN:722,(anonymous_15) +FN:735,(anonymous_16) +FN:742,(anonymous_17) +FN:749,(anonymous_18) +FN:756,(anonymous_19) +FN:764,(anonymous_20) +FN:768,(anonymous_21) +FN:770,(anonymous_22) +FN:783,(anonymous_23) +FN:790,(anonymous_24) +FN:796,(anonymous_25) +FN:802,(anonymous_26) +FN:807,(anonymous_27) +FN:815,(anonymous_28) +FN:821,(anonymous_29) +FN:830,(anonymous_30) +FN:836,(anonymous_31) +FN:836,(anonymous_32) +FN:854,(anonymous_33) +FN:860,(anonymous_34) +FN:870,(anonymous_35) +FN:874,(anonymous_36) +FN:914,(anonymous_37) +FN:930,(anonymous_38) +FN:949,(anonymous_39) +FN:958,(anonymous_40) +FN:960,(anonymous_41) +FN:971,(anonymous_42) +FN:985,(anonymous_43) +FN:1013,(anonymous_44) +FN:1043,(anonymous_45) +FN:1054,hasTorch +FNF:47 +FNH:11 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:12,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:2,(anonymous_7) +FNDA:1,(anonymous_8) +FNDA:1,(anonymous_9) +FNDA:2,(anonymous_10) +FNDA:2,(anonymous_11) +FNDA:1,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:3,(anonymous_27) +FNDA:0,(anonymous_28) +FNDA:0,(anonymous_29) +FNDA:0,(anonymous_30) +FNDA:3,(anonymous_31) +FNDA:5,(anonymous_32) +FNDA:0,(anonymous_33) +FNDA:0,(anonymous_34) +FNDA:0,(anonymous_35) +FNDA:0,(anonymous_36) +FNDA:0,(anonymous_37) +FNDA:0,(anonymous_38) +FNDA:0,(anonymous_39) +FNDA:0,(anonymous_40) +FNDA:0,(anonymous_41) +FNDA:0,(anonymous_42) +FNDA:0,(anonymous_43) +FNDA:0,(anonymous_44) +FNDA:0,(anonymous_45) +FNDA:2,hasTorch +DA:19,4 +DA:27,4 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:42,0 +DA:43,0 +DA:45,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:62,0 +DA:63,0 +DA:65,0 +DA:67,0 +DA:69,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:82,0 +DA:88,4 +DA:317,4 +DA:327,4 +DA:333,4 +DA:397,4 +DA:399,4 +DA:400,0 +DA:401,0 +DA:402,0 +DA:404,0 +DA:408,4 +DA:433,4 +DA:447,4 +DA:509,4 +DA:550,12 +DA:557,12 +DA:558,12 +DA:559,12 +DA:560,12 +DA:561,12 +DA:569,0 +DA:570,0 +DA:572,0 +DA:573,0 +DA:576,0 +DA:577,0 +DA:578,0 +DA:579,0 +DA:580,0 +DA:581,0 +DA:583,0 +DA:589,0 +DA:590,0 +DA:593,0 +DA:594,0 +DA:597,0 +DA:598,0 +DA:601,0 +DA:602,0 +DA:603,0 +DA:604,0 +DA:605,0 +DA:608,0 +DA:611,0 +DA:612,0 +DA:618,2 +DA:619,0 +DA:620,2 +DA:621,1 +DA:623,1 +DA:628,1 +DA:629,0 +DA:630,1 +DA:631,0 +DA:632,1 +DA:633,1 +DA:634,1 +DA:635,1 +DA:636,1 +DA:639,0 +DA:640,0 +DA:647,2 +DA:648,0 +DA:649,2 +DA:650,1 +DA:652,1 +DA:656,12 +DA:657,2 +DA:658,0 +DA:659,2 +DA:660,1 +DA:662,1 +DA:666,12 +DA:668,1 +DA:672,0 +DA:673,0 +DA:674,0 +DA:675,0 +DA:677,0 +DA:678,0 +DA:679,0 +DA:680,0 +DA:681,0 +DA:682,0 +DA:684,0 +DA:690,0 +DA:691,0 +DA:693,0 +DA:697,0 +DA:698,0 +DA:700,0 +DA:704,0 +DA:707,0 +DA:708,0 +DA:713,0 +DA:716,0 +DA:717,0 +DA:720,0 +DA:721,0 +DA:722,0 +DA:723,0 +DA:724,0 +DA:726,0 +DA:729,0 +DA:730,0 +DA:736,0 +DA:737,0 +DA:739,0 +DA:743,0 +DA:744,0 +DA:746,0 +DA:750,0 +DA:751,0 +DA:753,0 +DA:757,0 +DA:758,0 +DA:761,0 +DA:765,0 +DA:766,0 +DA:768,0 +DA:769,0 +DA:770,0 +DA:771,0 +DA:772,0 +DA:774,0 +DA:777,0 +DA:778,0 +DA:784,0 +DA:785,0 +DA:787,0 +DA:790,12 +DA:791,0 +DA:792,0 +DA:796,12 +DA:797,0 +DA:798,0 +DA:802,12 +DA:803,0 +DA:804,0 +DA:807,12 +DA:808,3 +DA:809,1 +DA:811,3 +DA:812,1 +DA:815,12 +DA:816,0 +DA:817,0 +DA:821,12 +DA:822,0 +DA:823,0 +DA:830,12 +DA:831,0 +DA:832,0 +DA:836,12 +DA:837,5 +DA:838,5 +DA:844,1 +DA:847,4 +DA:848,3 +DA:849,3 +DA:850,3 +DA:854,12 +DA:855,0 +DA:856,0 +DA:860,12 +DA:861,0 +DA:862,0 +DA:863,0 +DA:865,0 +DA:866,0 +DA:871,0 +DA:880,0 +DA:882,0 +DA:883,0 +DA:884,0 +DA:886,0 +DA:889,0 +DA:894,0 +DA:901,0 +DA:908,0 +DA:911,0 +DA:918,0 +DA:919,0 +DA:920,0 +DA:923,0 +DA:934,0 +DA:935,0 +DA:936,0 +DA:939,0 +DA:949,12 +DA:950,0 +DA:951,0 +DA:952,0 +DA:954,0 +DA:958,12 +DA:960,12 +DA:961,0 +DA:962,0 +DA:968,0 +DA:972,0 +DA:974,0 +DA:975,0 +DA:986,0 +DA:987,0 +DA:1006,0 +DA:1007,0 +DA:1009,0 +DA:1014,0 +DA:1016,0 +DA:1017,0 +DA:1020,0 +DA:1021,0 +DA:1024,0 +DA:1025,0 +DA:1028,0 +DA:1029,0 +DA:1032,0 +DA:1033,0 +DA:1036,0 +DA:1037,0 +DA:1040,0 +DA:1044,0 +DA:1045,0 +DA:1048,0 +DA:1052,4 +DA:1055,2 +DA:1056,2 +DA:1058,0 +LF:259 +LH:67 +BRDA:35,0,0,0 +BRDA:35,0,1,0 +BRDA:37,1,0,0 +BRDA:37,1,1,0 +BRDA:42,2,0,0 +BRDA:42,2,1,0 +BRDA:47,3,0,0 +BRDA:47,3,1,0 +BRDA:49,4,0,0 +BRDA:49,4,1,0 +BRDA:53,5,0,0 +BRDA:53,5,1,0 +BRDA:54,6,0,0 +BRDA:54,6,1,0 +BRDA:56,7,0,0 +BRDA:56,7,1,0 +BRDA:57,8,0,0 +BRDA:57,8,1,0 +BRDA:62,9,0,0 +BRDA:62,9,1,0 +BRDA:67,10,0,0 +BRDA:67,10,1,0 +BRDA:75,11,0,0 +BRDA:75,11,1,0 +BRDA:77,12,0,0 +BRDA:77,12,1,0 +BRDA:439,13,0,4 +BRDA:439,13,1,0 +BRDA:440,14,0,4 +BRDA:440,14,1,0 +BRDA:441,15,0,4 +BRDA:441,15,1,0 +BRDA:442,16,0,4 +BRDA:442,16,1,0 +BRDA:443,17,0,4 +BRDA:443,17,1,0 +BRDA:444,18,0,4 +BRDA:444,18,1,0 +BRDA:518,19,0,4 +BRDA:518,19,1,0 +BRDA:520,20,0,4 +BRDA:520,20,1,4 +BRDA:520,21,0,4 +BRDA:520,21,1,0 +BRDA:522,22,0,4 +BRDA:522,22,1,4 +BRDA:522,23,0,4 +BRDA:522,23,1,0 +BRDA:524,24,0,4 +BRDA:524,24,1,0 +BRDA:524,25,0,4 +BRDA:524,25,1,0 +BRDA:525,26,0,4 +BRDA:525,26,1,0 +BRDA:525,27,0,4 +BRDA:525,27,1,0 +BRDA:569,28,0,0 +BRDA:569,28,1,0 +BRDA:572,29,0,0 +BRDA:572,29,1,0 +BRDA:576,30,0,0 +BRDA:576,30,1,0 +BRDA:577,31,0,0 +BRDA:577,31,1,0 +BRDA:580,32,0,0 +BRDA:580,32,1,0 +BRDA:581,33,0,0 +BRDA:581,33,1,0 +BRDA:589,34,0,0 +BRDA:589,34,1,0 +BRDA:593,35,0,0 +BRDA:593,35,1,0 +BRDA:597,36,0,0 +BRDA:597,36,1,0 +BRDA:618,37,0,0 +BRDA:618,37,1,2 +BRDA:620,38,0,1 +BRDA:620,38,1,1 +BRDA:628,39,0,0 +BRDA:628,39,1,1 +BRDA:630,40,0,0 +BRDA:630,40,1,1 +BRDA:632,41,0,1 +BRDA:632,41,1,0 +BRDA:647,42,0,0 +BRDA:647,42,1,2 +BRDA:649,43,0,1 +BRDA:649,43,1,1 +BRDA:657,44,0,0 +BRDA:657,44,1,2 +BRDA:659,45,0,1 +BRDA:659,45,1,1 +BRDA:672,46,0,0 +BRDA:672,46,1,0 +BRDA:672,47,0,0 +BRDA:672,47,1,0 +BRDA:674,48,0,0 +BRDA:674,48,1,0 +BRDA:677,49,0,0 +BRDA:677,49,1,0 +BRDA:678,50,0,0 +BRDA:678,50,1,0 +BRDA:681,51,0,0 +BRDA:681,51,1,0 +BRDA:682,52,0,0 +BRDA:682,52,1,0 +BRDA:690,53,0,0 +BRDA:690,53,1,0 +BRDA:691,54,0,0 +BRDA:691,54,1,0 +BRDA:691,55,0,0 +BRDA:691,55,1,0 +BRDA:700,56,0,0 +BRDA:700,56,1,0 +BRDA:701,57,0,0 +BRDA:701,57,1,0 +BRDA:707,58,0,0 +BRDA:707,58,1,0 +BRDA:708,59,0,0 +BRDA:708,59,1,0 +BRDA:709,60,0,0 +BRDA:709,60,1,0 +BRDA:709,60,2,0 +BRDA:716,61,0,0 +BRDA:716,61,1,0 +BRDA:736,62,0,0 +BRDA:736,62,1,0 +BRDA:743,63,0,0 +BRDA:743,63,1,0 +BRDA:750,64,0,0 +BRDA:750,64,1,0 +BRDA:757,65,0,0 +BRDA:757,65,1,0 +BRDA:765,66,0,0 +BRDA:765,66,1,0 +BRDA:784,67,0,0 +BRDA:784,67,1,0 +BRDA:791,68,0,0 +BRDA:791,68,1,0 +BRDA:797,69,0,0 +BRDA:797,69,1,0 +BRDA:803,70,0,0 +BRDA:803,70,1,0 +BRDA:808,71,0,1 +BRDA:808,71,1,2 +BRDA:808,72,0,3 +BRDA:808,72,1,2 +BRDA:811,73,0,1 +BRDA:811,73,1,2 +BRDA:811,74,0,3 +BRDA:811,74,1,2 +BRDA:816,75,0,0 +BRDA:816,75,1,0 +BRDA:822,76,0,0 +BRDA:822,76,1,0 +BRDA:831,77,0,0 +BRDA:831,77,1,0 +BRDA:838,78,0,1 +BRDA:838,78,1,4 +BRDA:839,79,0,5 +BRDA:839,79,1,2 +BRDA:839,79,2,2 +BRDA:839,79,3,1 +BRDA:847,80,0,3 +BRDA:847,80,1,1 +BRDA:855,81,0,0 +BRDA:855,81,1,0 +BRDA:861,82,0,0 +BRDA:861,82,1,0 +BRDA:884,83,0,0 +BRDA:884,83,1,0 +BRDA:884,84,0,0 +BRDA:884,84,1,0 +BRDA:908,85,0,0 +BRDA:908,85,1,0 +BRDA:919,86,0,0 +BRDA:919,86,1,0 +BRDA:935,87,0,0 +BRDA:935,87,1,0 +BRDA:951,88,0,0 +BRDA:951,88,1,0 +BRDA:954,89,0,0 +BRDA:954,89,1,0 +BRDA:961,90,0,0 +BRDA:961,90,1,0 +BRDA:974,91,0,0 +BRDA:974,91,1,0 +BRDA:974,92,0,0 +BRDA:974,92,1,0 +BRDA:1006,93,0,0 +BRDA:1006,93,1,0 +BRDA:1016,94,0,0 +BRDA:1016,94,1,0 +BRDA:1020,95,0,0 +BRDA:1020,95,1,0 +BRDA:1024,96,0,0 +BRDA:1024,96,1,0 +BRDA:1028,97,0,0 +BRDA:1028,97,1,0 +BRDA:1028,98,0,0 +BRDA:1028,98,1,0 +BRDA:1032,99,0,0 +BRDA:1032,99,1,0 +BRDA:1036,100,0,0 +BRDA:1036,100,1,0 +BRDA:1044,101,0,0 +BRDA:1044,101,1,0 +BRDA:1044,102,0,0 +BRDA:1044,102,1,0 +BRDA:1055,103,0,2 +BRDA:1055,103,1,0 +BRF:211 +BRH:46 +end_of_record +TN: +SF:src\index.js +FNF:0 +FNH:0 +LF:0 +LH:0 +BRF:0 +BRH:0 +end_of_record diff --git a/jest/helpers/nativeCameraMock.js b/jest/helpers/nativeCameraMock.js new file mode 100644 index 000000000..d715b004e --- /dev/null +++ b/jest/helpers/nativeCameraMock.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * src/NativeCamera(codegen 组件 spec)的 jest mock。 + * + * 源文件依赖 codegenNativeComponent / codegenNativeCommands(RN 内部模块), + * 测试侧整体替换为 stub 组件 + Commands jest.fn。 + */ + +const Commands = { + takePicture: jest.fn(), + record: jest.fn(), + stopRecording: jest.fn(), + pauseRecording: jest.fn(), + resumeRecording: jest.fn(), + pausePreview: jest.fn(), + resumePreview: jest.fn(), + getSupportedRatios: jest.fn(), + getSupportedPreviewFpsRange: jest.fn(), +}; + +const NativeCamera = function NativeCamera() { + return null; +}; + +module.exports = NativeCamera; +module.exports.default = NativeCamera; +module.exports.Commands = Commands; +module.exports.RecordResponse = {}; diff --git a/jest/helpers/nativeCameraModuleMock.js b/jest/helpers/nativeCameraModuleMock.js new file mode 100644 index 000000000..693187923 --- /dev/null +++ b/jest/helpers/nativeCameraModuleMock.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * src/NativeCameraModule(TurboModule spec)的 jest mock。 + * + * 全方法 jest.fn,默认返回已 resolve 的 Promise,便于透传断言; + * 测试侧可用 RNCameraModuleMock 控制返回值。 + */ + +const createOk = (value) => jest.fn(async () => value); + +const NativeCameraModule = { + getCameraPermission: createOk(true), + getAuidPermission: createOk(true), + getCameraIds: jest.fn(async () => [{ id: "cam0", type: 0 }]), + getAvailablePictureSizes: jest.fn(async () => ["640x480", "1280x720"]), + hasTorch: createOk(true), + checkIfVideoIsValid: createOk(true), + getSupportedPreviewFpsRange: jest.fn(async () => ["15,30", "30,30"]), + getSupportedRatios: jest.fn(async () => ["4:3", "16:9"]), +}; + +module.exports = NativeCameraModule; +module.exports.RNCameraModuleMock = NativeCameraModule; diff --git a/jest/helpers/nativeFaceDetectorMock.js b/jest/helpers/nativeFaceDetectorMock.js new file mode 100644 index 000000000..24f52f26e --- /dev/null +++ b/jest/helpers/nativeFaceDetectorMock.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * src/NativeFaceDetector(TurboModule spec)的 jest mock。 + */ + +const NativeFaceDetector = { + detectFaces: jest.fn(async () => []), +}; + +module.exports = NativeFaceDetector; diff --git a/jest/helpers/propTypesMock.js b/jest/helpers/propTypesMock.js new file mode 100644 index 000000000..a7ed57d48 --- /dev/null +++ b/jest/helpers/propTypesMock.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * prop-types 的 jest mock(各测试文件顶部 + * `jest.mock("prop-types", () => require("../helpers/propTypesMock"))` 显式激活)。 + * + * RNCamera.js 用 PropTypes 声明 static propTypes(仅 dev 校验用),运行时只需要 + * 校验器对象存在。stub 成员取值返回可链式占位对象(.isRequired 继续可链)。 + */ + +function checker() { + const fn = function () {}; + fn.isRequired = fn; + return fn; +} + +const PropTypes = { + array: checker(), + bool: checker(), + func: checker(), + number: checker(), + object: checker(), + string: checker(), + symbol: checker(), + any: checker(), + element: checker(), + elementType: checker(), + instanceOf: checker, + enum: checker, + node: checker(), + objectOf: checker, + oneOf: checker, + oneOfType: checker, + arrayOf: checker, + exact: checker, + shape: checker, +}; + +module.exports = PropTypes; diff --git a/jest/helpers/reactMock.js b/jest/helpers/reactMock.js new file mode 100644 index 000000000..7ce525bd6 --- /dev/null +++ b/jest/helpers/reactMock.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * react 的 jest mock(各测试文件顶部 + * `jest.mock("react", () => require("../helpers/reactMock"))` 显式激活)。 + * + * RNCamera.js 是 class 组件 + JSX。Component stub 提供可用的 + * constructor/setState/forceUpdate,让测试能直接实例化 Camera 类。 + * JSX 由 babel 的 @babel/plugin-transform-react-jsx 转为 createElement, + * mock 的 createElement 返回描述对象即可(测试不渲染)。 + */ + +class Component { + constructor(props) { + this.props = props || {}; + this.state = {}; + } + setState(partial, callback) { + const next = typeof partial === "function" ? partial(this.state, this.props) : partial; + this.state = { ...this.state, ...next }; + if (callback) callback(); + } + forceUpdate() {} +} + +const React = { + Component, + PureComponent: Component, + memo: (x) => x, + createElement: jest.fn((type, props, ...children) => ({ type, props, children })), + cloneElement: jest.fn((el, props) => ({ ...el, props: { ...el.props, ...props } })), + useRef: jest.fn((init) => ({ current: init })), + useState: jest.fn((init) => [init, jest.fn()]), + useEffect: jest.fn(), + useCallback: jest.fn((fn) => fn), + Fragment: "Fragment", +}; + +module.exports = React; +module.exports.default = React; diff --git a/jest/helpers/reactNativeMock.js b/jest/helpers/reactNativeMock.js new file mode 100644 index 000000000..30fc51e6f --- /dev/null +++ b/jest/helpers/reactNativeMock.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * react-native 的 jest mock(各测试文件顶部 + * `jest.mock("react-native", () => require("../helpers/reactNativeMock"))` 显式激活)。 + * + * 覆盖 RNCamera.js / FaceDetector.js / index.js 的 react-native 依赖面: + * Platform / DeviceEventEmitter / findNodeHandle / PermissionsAndroid / + * ViewPropTypes / View / ActivityIndicator / Text / StyleSheet。 + * 每个 Jest 测试文件是独立 module registry,本文件在每个测试文件中各执行一次。 + */ + +// 平台态可切换:测试 iOS 分支时用 RNCameraJestMock.setPlatformOS("ios") +let currentOS = "harmony"; + +const Platform = { + get OS() { + return currentOS; + }, + select: (specifics) => specifics[currentOS] || specifics.ios, +}; + +// 事件名 → 最近注册的 handler(供测试侧 emit 触发) +const handlers = new Map(); +// 事件名 → 最近返回的订阅句柄(供测试侧断言 remove) +const subsByEvent = new Map(); + +const DeviceEventEmitter = { + addListener: jest.fn((eventName, handler) => { + handlers.set(eventName, handler); + const sub = { remove: jest.fn() }; + subsByEvent.set(eventName, sub); + return sub; + }), +}; + +const PermissionsAndroid = { + REQUEST_INSTALL_PACKAGES: undefined, + RESULTS: { GRANTED: "granted", DENIED: "denied", NEVER_ASK_AGAIN: "never_ask_again" }, + request: jest.fn(async () => PermissionsAndroid.RESULTS.GRANTED), + requestMultiple: jest.fn(async () => ({ "android.permission.CAMERA": "granted" })), + check: jest.fn(async () => true), +}; + +function StyleStub() {} + +module.exports = { + Platform, + DeviceEventEmitter, + findNodeHandle: jest.fn(() => 1), + PermissionsAndroid, + ViewPropTypes: new Proxy({}, { get: () => ({}), has: () => true }), + View: StyleStub, + ActivityIndicator: StyleStub, + Text: StyleStub, + StyleSheet: { create: (styles) => styles }, + // 测试钩子命名空间 + RNCameraJestMock: { + setPlatformOS: (os) => { + currentOS = os; + }, + emit: (eventName, payload) => { + const handler = handlers.get(eventName); + if (!handler) return false; + handler(payload); + return true; + }, + getSubscription: (eventName) => subsByEvent.get(eventName), + }, +}; diff --git a/jest/jest.config.js b/jest/jest.config.js new file mode 100644 index 000000000..26c427574 --- /dev/null +++ b/jest/jest.config.js @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * RNOH JS 层白盒测试基础设施(@react-native-ohos/react-native-camera) + * + * 按 rnoh-js-test skill 方法论生成(仿 @react-native-ohos/react-native-sensors 落地形态): + * - babel-jest + @babel/preset-flow 转译方案(hermes parser 解析 Flow/ESM/JSX) + * - testMatch 精确匹配连字符命名 `*-test.js` + * - `__DEV__` globals、transformIgnorePatterns、coverage 白名单 + * + * 工具链解析:库根 devDependencies 声明 jest@29.7.0 与 @babel/* 工具链, + * 装入库根 node_modules,createRequire 锚定库根解析为绝对路径。 + * + * 运行(在库根目录执行): + * npm test # jest --no-coverage + * npm run test:coverage # jest --coverage + */ + +const path = require("path"); +const { createRequire } = require("module"); + +const toolchainRequire = createRequire(path.join(__dirname, "..", "node_modules", "_anchor_")); + +const babelJest = toolchainRequire.resolve("babel-jest"); +const presetEnv = toolchainRequire.resolve("@babel/preset-env"); +const presetFlow = toolchainRequire.resolve("@babel/preset-flow"); +const hermesParserPlugin = toolchainRequire.resolve("babel-plugin-syntax-hermes-parser"); +const jsxPlugin = toolchainRequire.resolve("@babel/plugin-transform-react-jsx"); + +module.exports = { + // rootDir 指向库根:coverage 以库根为基准收集 src/*.js + rootDir: path.resolve(__dirname, ".."), + + // roots 限制在本测试目录:库根全树有 harmony 等(上万文件), + // 不让 jest 参与 haste 扫描,避免拖慢启动 + roots: [path.resolve(__dirname)], + + // skill C2:testMatch 精确匹配连字符命名 *-test.js + testMatch: ["/jest/__tests__/*-test.js"], + + // skill C3:排除 node_modules 与 oh_modules + testPathIgnorePatterns: ["/node_modules/", "/oh_modules/", "/harmony/"], + + transform: { + "^.+\.js$": [ + babelJest, + { + // 显式禁用 babel 配置文件查找:绝不加载库根上游遗留的 babel 配置 + configFile: false, + babelrc: false, + presets: [ + // ESM → CommonJS,node 环境直跑 + [presetEnv, { targets: { node: "current" }, modules: "commonjs" }], + // Flow 类型剥离 + [presetFlow, { all: true }], + ], + plugins: [ + // hermes parser:解析 Flow/ESM/JSX 语法(仅语法,不做转换) + [hermesParserPlugin, { parseLangTypes: "flow" }], + // JSX 转换(RNCamera.js 的 render 含 JSX) + [jsxPlugin], + ], + }, + ], + }, + + // skill 关键配置:RN 内部代码检查 __DEV__ 全局变量 + globals: { + __DEV__: true, + }, + + transformIgnorePatterns: ["node_modules/(?!react-native|@react-native|react)"], + + // 全局 mock/兜底 + setupFiles: ["/jest/jest.setup.js"], + + // coverage 白名单:本库 JS 层生产文件(src/*.ts 为 codegen spec 声明,无独立运行时逻辑) + collectCoverageFrom: ["src/*.js"], + coverageDirectory: "/jest/coverage", + coverageThreshold: { + global: { + // RNCamera.js 为千行 UI 组件(render/生命周期胶水占比高),白盒测试聚焦 + // 逻辑分支(平台路由/常量/事件节流/导出面),门槛按该定位设定 + branches: 20, + }, + }, +}; diff --git a/jest/jest.setup.js b/jest/jest.setup.js new file mode 100644 index 000000000..fb319544b --- /dev/null +++ b/jest/jest.setup.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2026 Huawei Technologies Co., Ltd. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +"use strict"; + +/** + * Jest 全局 setup(rnoh-js-test skill 的 setupFiles 配置位)。 + * + * camera 的 JS 层依赖面在测试侧由各测试文件显式 jest.mock 隔离: + * 1. react-native(Platform/DeviceEventEmitter/findNodeHandle/组件等)—— helpers/reactNativeMock.js + * 2. prop-types —— helpers/propTypesMock.js + * 3. react —— helpers/reactMock.js + * 4. src/NativeCameraModule(TurboModule spec)—— helpers/nativeCameraModuleMock.js + * 5. src/NativeCamera(codegen 组件 spec + Commands)—— helpers/nativeCameraMock.js + * 6. src/NativeFaceDetector —— helpers/nativeFaceDetectorMock.js + * + * 库源码检查 __DEV__(由 jest.config.js 的 globals 提供),其余 RN 全局未引用。 + */ diff --git a/package.json b/package.json index f756b4b6c..3c1e766b5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@react-native-ohos/react-native-camera", "description": "A Camera component for React Native. Also reads barcodes.", - "version": "3.42.1-rc.1", + "version": "4.0.0-beta.1", "author": "Lochlan Wansbrough (http://lwansbrough.com)", "collective": { "type": "opencollective", @@ -13,6 +13,13 @@ }, "devDependencies": { "@babel/runtime": "^7.3.1", + "@babel/core": "*", + "@babel/preset-env": "^7.24.0", + "@babel/preset-flow": "^7.24.0", + "@babel/plugin-transform-react-jsx": "^7.24.0", + "babel-jest": "^29.7.0", + "babel-plugin-syntax-hermes-parser": "^0.25.0", + "jest": "^29.7.0", "@commitlint/cli": "^9.1.2", "@commitlint/config-conventional": "^9.1.2", "@commitlint/prompt": "^9.1.2", @@ -42,12 +49,15 @@ "metro": "0.82.5", "memfs": "4.17.2" }, - "homepage": "https://github.com/react-native-community/react-native-camera", + "homepage": "https://github.com/react-native-oh-library/react-native-camera", "keywords": [ "barcode", "camera", "code", + "harmony", "native", + "ohos", + "openharmony", "qr", "react", "react-native" @@ -73,7 +83,7 @@ "pre-commit": "lint:staged", "repository": { "type": "git", - "url": "https://github.com/react-native-community/react-native-camera.git" + "url": "https://github.com/react-native-oh-library/react-native-camera.git" }, "scripts": { "commit": "git-cz", @@ -81,6 +91,8 @@ "lint": "eslint src --max-warnings=0", "lint:staged": "lint-staged", "prettier": "prettier --write", + "test": "jest --no-coverage", + "test:coverage": "jest --coverage", "ci:publish": "semantic-release", "codegen-lib": "react-native codegen-lib-harmony --no-safety-check --npm-package-name react-native-camera --cpp-components-spec-paths ./src --turbo-modules-spec-paths ./src --cpp-output-path ./harmony/reactNativeCamera/src/main/cpp/generated --ets-output-path ./harmony/reactNativeCamera/src/main/ets/generated" }, diff --git a/src/NativeCamera.ts b/src/NativeCamera.ts index 3adb39e6b..fafd072b7 100644 --- a/src/NativeCamera.ts +++ b/src/NativeCamera.ts @@ -290,6 +290,7 @@ type FlashMode = 'on' | 'off' | 'torch' | 'auto'; export interface NativeProps extends ViewProps { autoFocus?: WithDefault, autoFocusPointOfInterest?: { x: Int32, y: Int32 }, + cameraId?: string, pictureSize?: string, type?: WithDefault, flashMode?: WithDefault, @@ -315,6 +316,7 @@ export interface NativeProps extends ViewProps { videoStabilizationMode?: WithDefault, whiteBalance?: WithDefault, playSoundOnCapture?: boolean, + playSoundOnRecord?: boolean, barCodeScannerEnabled: boolean, touchDetectorEnabled: boolean, googleVisionBarcodeDetectorEnabled: boolean, diff --git a/src/NativeCameraModule.ts b/src/NativeCameraModule.ts index 7bac6e3aa..b06f8b47c 100644 --- a/src/NativeCameraModule.ts +++ b/src/NativeCameraModule.ts @@ -11,6 +11,10 @@ export interface Spec extends TurboModule { getAuidPermission(): Promise; getCameraIds():Promise; getAvailablePictureSizes():Promise; + hasTorch():Promise; + checkIfVideoIsValid(path: string): Promise; + getSupportedPreviewFpsRange(handle: number): Promise; + getSupportedRatios(handle: number): Promise; } export default TurboModuleRegistry.get('RNCCameraModule'); diff --git a/src/RNCamera.js b/src/RNCamera.js index 8f801e02a..d7f80df37 100644 --- a/src/RNCamera.js +++ b/src/RNCamera.js @@ -111,6 +111,8 @@ type PictureOptions = { fixOrientation?: boolean, forceUpOrientation?: boolean, pauseAfterCapture?: boolean, + imageType?: 'jpeg' | 'png', + path?: string, }; type TrackedFaceFeature = FaceFeature & { @@ -225,6 +227,17 @@ type Phone = { 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, @@ -286,8 +299,10 @@ type PropsType = typeof View.props & { keepAudioSession ?: boolean, useCamera2Api ?: boolean, playSoundOnCapture ?: boolean, + playSoundOnRecord ?: boolean, videoStabilizationMode ?: number | string, pictureSize ?: string, + cameraId ?: string, rectOfInterest: Rect, }; @@ -318,15 +333,27 @@ const RecordAudioPermissionStatusEnum: { const CameraManager: Object = { stubbed: true, Type: { - back: 1, + back: 'back', + front: 'front', }, AutoFocus: { - on: 1, + on: 'on', + off: 'off', }, FlashMode: { - off: 1, + off: 'off', + on: 'on', + torch: 'torch', + auto: 'auto', + }, + WhiteBalance: { + sunny: 'sunny', + cloudy: 'cloudy', + shadow: 'shadow', + incandescent: 'incandescent', + fluorescent: 'fluorescent', + auto: 'auto', }, - WhiteBalance: {}, BarCodeType: {}, FaceDetection: { fast: 1, @@ -342,6 +369,29 @@ const CameraManager: Object = { 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; @@ -361,6 +411,7 @@ export default class Camera extends React.Component { AutoFocus: CameraManager.AutoFocus, WhiteBalance: CameraManager.WhiteBalance, VideoQuality: CameraManager.VideoQuality, + ImageType: CameraManager.ImageType, VideoCodec: CameraManager.VideoCodec, BarCodeType: CameraManager.BarCodeType, GoogleVisionBarcodeDetection: CameraManager.GoogleVisionBarcodeDetection, @@ -446,6 +497,7 @@ export default class Camera extends React.Component { 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, @@ -459,7 +511,7 @@ export default class Camera extends React.Component { ratio: '4:3', focusDepth: 0, type: "back", - cameraId: null, + cameraId: '', autoFocus: CameraManager.AutoFocus.on, flashMode: CameraManager.FlashMode.off, whiteBalance: CameraManager.WhiteBalance.auto, @@ -489,8 +541,9 @@ export default class Camera extends React.Component { keepAudioSession: false, useCamera2Api: false, playSoundOnCapture: false, + playSoundOnRecord: false, pictureSize: 'None', - videoStabilizationMode: 0, + videoStabilizationMode: 'off', // harmony: cpp 枚举按字符串解析("off"/"standard"/"cinematic"/"auto") mirrorVideo: false, }; @@ -564,6 +617,8 @@ export default class Camera extends React.Component { async getSupportedRatiosAsync() { if (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'); } @@ -573,7 +628,7 @@ export default class Camera extends React.Component { if (Platform.OS === 'android') { return await CameraManager.getCameraIds(this._cameraHandle); } else if (Platform.OS === 'ios') { - return await CameraNativeCameraModuleManager.getCameraIds(); // iOS does not need a camera instance + return await CameraManager.getCameraIds(); // iOS does not need a camera instance } else if (Platform.OS === 'harmony') { return new Promise(async (resolve, reject) => { try { @@ -588,9 +643,21 @@ export default class Camera extends React.Component { } } + static async checkIfVideoIsValid(path) { + if (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<[]> => { if (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'); } @@ -920,7 +987,7 @@ export default class Camera extends React.Component { this._cameraHandle = findNodeHandle(ref); }} onMountError={this._onMountError} - onCameraReady={this._onCameraReady} + onCameraReady={this._onObjectDetected(this._onCameraReady)} onAudioInterrupted={this._onAudioInterrupted} onAudioConnected={this._onAudioConnected} onGoogleVisionBarcodesDetected={this._onObjectDetected( @@ -982,4 +1049,11 @@ export default class Camera extends React.Component { } } -export const Constants = Camera.Constants; \ No newline at end of file +export const Constants = Camera.Constants; + +export function hasTorch() { + if (Platform.OS === 'harmony') { + return NativeCameraModule.hasTorch(); + } + return CameraManager.hasTorch(); +} diff --git a/src/index.js b/src/index.js index 6a81d8b75..01f84872b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ // @flow -import RNCamera, { type Status as _CameraStatus } from './RNCamera'; +import RNCamera, { type Status as _CameraStatus, hasTorch } from './RNCamera'; import FaceDetector from './FaceDetector'; export type CameraStatus = _CameraStatus; -export { RNCamera, FaceDetector }; +export { RNCamera, FaceDetector, hasTorch }; diff --git a/types/index.d.ts b/types/index.d.ts index 41e0c5855..8aa925e8e 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -72,6 +72,10 @@ type VideoCodec = Readonly<{ AppleProRes422: symbol; AppleProRes4444: symbol; }>; +type ImageType = Readonly<{ + 'jpeg': any; + 'png': any; +}>; type FaceDetectionClassifications = Readonly<{ all: any; none: any }>; type FaceDetectionLandmarks = Readonly<{ all: any; none: any }>; @@ -119,6 +123,7 @@ export interface Constants { Type: CameraType; WhiteBalance: WhiteBalance; VideoQuality: VideoQuality; + ImageType: ImageType; BarCodeType: BarCodeType; FaceDetection: { Classifications: FaceDetectionClassifications; @@ -168,6 +173,7 @@ export interface GoogleVisionBarcodesDetectedEvent { export interface RNCameraProps { children?: ReactNode | FaCC; + cameraId?: string; autoFocus?: keyof AutoFocus; autoFocusPointOfInterest?: Point; @@ -227,7 +233,7 @@ export interface RNCameraProps { onGoogleVisionBarcodesDetected?(event: GoogleVisionBarcodesDetectedEvent): void; // limiting scan area - rectOfInterest?: Point; + rectOfInterest?: RectOfInterest; // -- FACE DETECTION PROPS @@ -248,6 +254,8 @@ export interface RNCameraProps { permissionDialogMessage?: string; /** Android only */ playSoundOnCapture?: boolean; + /** Android only */ + playSoundOnRecord?: boolean; androidCameraPermissionOptions?: { title: string; @@ -285,6 +293,8 @@ interface Size { height: T; } +interface RectOfInterest extends Point,Size{} + export interface Barcode { bounds: { size: Size; @@ -425,6 +435,8 @@ interface TakePictureOptions { /** iOS only */ forceUpOrientation?: boolean; + imageType?: keyof ImageType; + path?: string; } export interface TakePictureResponse { @@ -462,6 +474,15 @@ export interface RecordResponse { codec: VideoCodec[keyof VideoCodec]; } +export interface HardwareCamera { + /** (iOS only) e.g: 'AVCaptureDeviceTypeBuiltInWideAngleCamera', 'AVCaptureDeviceTypeBuiltInUltraWideCamera' */ + deviceType?: string; + id: string; + type: number; +} + +export function hasTorch(): Promise; + export class RNCamera extends Component { static Constants: Constants; @@ -475,9 +496,12 @@ export class RNCamera extends Component { pausePreview(): void; resumePreview(): void; getAvailablePictureSizes(): Promise; + getCameraIdsAsync(): Promise; /** Android only */ getSupportedRatiosAsync(): Promise; + getSupportedPreviewFpsRange: Promise; + static checkIfVideoIsValid: Promise; /** iOS only */ isRecording(): Promise;