@chipmobilesdk/rn-camera 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +152 -0
- package/README.md +47 -14
- package/lib/commonjs/config/types.js +54 -0
- package/lib/commonjs/config/types.js.map +1 -0
- package/lib/commonjs/core/capabilities.js +45 -0
- package/lib/commonjs/core/capabilities.js.map +1 -0
- package/lib/commonjs/core/captureController.js +107 -0
- package/lib/commonjs/core/captureController.js.map +1 -0
- package/lib/commonjs/core/contract.js +16 -0
- package/lib/commonjs/core/contract.js.map +1 -0
- package/lib/commonjs/core/controller.js +452 -0
- package/lib/commonjs/core/controller.js.map +1 -0
- package/lib/commonjs/core/sessionMachine.js +116 -0
- package/lib/commonjs/core/sessionMachine.js.map +1 -0
- package/lib/commonjs/core/sessionRuntime.js +218 -0
- package/lib/commonjs/core/sessionRuntime.js.map +1 -0
- package/lib/commonjs/diagnostics/errors.js +86 -0
- package/lib/commonjs/diagnostics/errors.js.map +1 -0
- package/lib/commonjs/diagnostics/logger.js +40 -0
- package/lib/commonjs/diagnostics/logger.js.map +1 -0
- package/lib/commonjs/geometry/coordinateMapping.js +156 -0
- package/lib/commonjs/geometry/coordinateMapping.js.map +1 -0
- package/lib/commonjs/index.js +130 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/native/cameraEngine.js +86 -0
- package/lib/commonjs/native/cameraEngine.js.map +1 -0
- package/lib/commonjs/native/visionCameraPeer.js +28 -0
- package/lib/commonjs/native/visionCameraPeer.js.map +1 -0
- package/lib/commonjs/native/visionSurface.js +38 -0
- package/lib/commonjs/native/visionSurface.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/permissions/permissionPort.js +105 -0
- package/lib/commonjs/permissions/permissionPort.js.map +1 -0
- package/lib/commonjs/react/CameraPreview.js +136 -0
- package/lib/commonjs/react/CameraPreview.js.map +1 -0
- package/lib/commonjs/react/VisionCameraFeed.js +196 -0
- package/lib/commonjs/react/VisionCameraFeed.js.map +1 -0
- package/lib/commonjs/react/hooks.js +50 -0
- package/lib/commonjs/react/hooks.js.map +1 -0
- package/lib/commonjs/storage/artifactStore.js +98 -0
- package/lib/commonjs/storage/artifactStore.js.map +1 -0
- package/lib/commonjs/testing/conformance.js +109 -0
- package/lib/commonjs/testing/conformance.js.map +1 -0
- package/lib/commonjs/testing/index.js +50 -0
- package/lib/commonjs/testing/index.js.map +1 -0
- package/lib/commonjs/testing/testCameraAdapter.js +191 -0
- package/lib/commonjs/testing/testCameraAdapter.js.map +1 -0
- package/lib/module/config/types.js +49 -0
- package/lib/module/config/types.js.map +1 -0
- package/lib/module/core/capabilities.js +37 -0
- package/lib/module/core/capabilities.js.map +1 -0
- package/lib/module/core/captureController.js +98 -0
- package/lib/module/core/captureController.js.map +1 -0
- package/lib/module/core/contract.js +12 -0
- package/lib/module/core/contract.js.map +1 -0
- package/lib/module/core/controller.js +442 -0
- package/lib/module/core/controller.js.map +1 -0
- package/lib/module/core/sessionMachine.js +109 -0
- package/lib/module/core/sessionMachine.js.map +1 -0
- package/lib/module/core/sessionRuntime.js +215 -0
- package/lib/module/core/sessionRuntime.js.map +1 -0
- package/lib/module/diagnostics/errors.js +79 -0
- package/lib/module/diagnostics/errors.js.map +1 -0
- package/lib/module/diagnostics/logger.js +35 -0
- package/lib/module/diagnostics/logger.js.map +1 -0
- package/lib/module/geometry/coordinateMapping.js +149 -0
- package/lib/module/geometry/coordinateMapping.js.map +1 -0
- package/lib/module/index.js +44 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native/cameraEngine.js +83 -0
- package/lib/module/native/cameraEngine.js.map +1 -0
- package/lib/module/native/visionCameraPeer.js +24 -0
- package/lib/module/native/visionCameraPeer.js.map +1 -0
- package/lib/module/native/visionSurface.js +34 -0
- package/lib/module/native/visionSurface.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/permissions/permissionPort.js +101 -0
- package/lib/module/permissions/permissionPort.js.map +1 -0
- package/lib/module/react/CameraPreview.js +130 -0
- package/lib/module/react/CameraPreview.js.map +1 -0
- package/lib/module/react/VisionCameraFeed.js +190 -0
- package/lib/module/react/VisionCameraFeed.js.map +1 -0
- package/lib/module/react/hooks.js +45 -0
- package/lib/module/react/hooks.js.map +1 -0
- package/lib/module/storage/artifactStore.js +94 -0
- package/lib/module/storage/artifactStore.js.map +1 -0
- package/lib/module/testing/conformance.js +102 -0
- package/lib/module/testing/conformance.js.map +1 -0
- package/lib/module/testing/index.js +13 -0
- package/lib/module/testing/index.js.map +1 -0
- package/lib/module/testing/testCameraAdapter.js +185 -0
- package/lib/module/testing/testCameraAdapter.js.map +1 -0
- package/lib/typescript/commonjs/config/types.d.ts +122 -0
- package/lib/typescript/commonjs/config/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/capabilities.d.ts +16 -0
- package/lib/typescript/commonjs/core/capabilities.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/captureController.d.ts +48 -0
- package/lib/typescript/commonjs/core/captureController.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/contract.d.ts +92 -0
- package/lib/typescript/commonjs/core/contract.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/controller.d.ts +15 -0
- package/lib/typescript/commonjs/core/controller.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/sessionMachine.d.ts +46 -0
- package/lib/typescript/commonjs/core/sessionMachine.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/sessionRuntime.d.ts +64 -0
- package/lib/typescript/commonjs/core/sessionRuntime.d.ts.map +1 -0
- package/lib/typescript/commonjs/diagnostics/errors.d.ts +24 -0
- package/lib/typescript/commonjs/diagnostics/errors.d.ts.map +1 -0
- package/lib/typescript/commonjs/diagnostics/logger.d.ts +24 -0
- package/lib/typescript/commonjs/diagnostics/logger.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/coordinateMapping.d.ts +23 -0
- package/lib/typescript/commonjs/geometry/coordinateMapping.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +32 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/native/cameraEngine.d.ts +50 -0
- package/lib/typescript/commonjs/native/cameraEngine.d.ts.map +1 -0
- package/lib/typescript/commonjs/native/visionCameraPeer.d.ts +2 -0
- package/lib/typescript/commonjs/native/visionCameraPeer.d.ts.map +1 -0
- package/lib/typescript/commonjs/native/visionSurface.d.ts +41 -0
- package/lib/typescript/commonjs/native/visionSurface.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/permissions/permissionPort.d.ts +24 -0
- package/lib/typescript/commonjs/permissions/permissionPort.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/CameraPreview.d.ts +25 -0
- package/lib/typescript/commonjs/react/CameraPreview.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/VisionCameraFeed.d.ts +31 -0
- package/lib/typescript/commonjs/react/VisionCameraFeed.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/hooks.d.ts +12 -0
- package/lib/typescript/commonjs/react/hooks.d.ts.map +1 -0
- package/lib/typescript/commonjs/storage/artifactStore.d.ts +54 -0
- package/lib/typescript/commonjs/storage/artifactStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/testing/conformance.d.ts +37 -0
- package/lib/typescript/commonjs/testing/conformance.d.ts.map +1 -0
- package/lib/typescript/commonjs/testing/index.d.ts +12 -0
- package/lib/typescript/commonjs/testing/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/testing/testCameraAdapter.d.ts +61 -0
- package/lib/typescript/commonjs/testing/testCameraAdapter.d.ts.map +1 -0
- package/lib/typescript/module/config/types.d.ts +122 -0
- package/lib/typescript/module/config/types.d.ts.map +1 -0
- package/lib/typescript/module/core/capabilities.d.ts +16 -0
- package/lib/typescript/module/core/capabilities.d.ts.map +1 -0
- package/lib/typescript/module/core/captureController.d.ts +48 -0
- package/lib/typescript/module/core/captureController.d.ts.map +1 -0
- package/lib/typescript/module/core/contract.d.ts +92 -0
- package/lib/typescript/module/core/contract.d.ts.map +1 -0
- package/lib/typescript/module/core/controller.d.ts +15 -0
- package/lib/typescript/module/core/controller.d.ts.map +1 -0
- package/lib/typescript/module/core/sessionMachine.d.ts +46 -0
- package/lib/typescript/module/core/sessionMachine.d.ts.map +1 -0
- package/lib/typescript/module/core/sessionRuntime.d.ts +64 -0
- package/lib/typescript/module/core/sessionRuntime.d.ts.map +1 -0
- package/lib/typescript/module/diagnostics/errors.d.ts +24 -0
- package/lib/typescript/module/diagnostics/errors.d.ts.map +1 -0
- package/lib/typescript/module/diagnostics/logger.d.ts +24 -0
- package/lib/typescript/module/diagnostics/logger.d.ts.map +1 -0
- package/lib/typescript/module/geometry/coordinateMapping.d.ts +23 -0
- package/lib/typescript/module/geometry/coordinateMapping.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +32 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/native/cameraEngine.d.ts +50 -0
- package/lib/typescript/module/native/cameraEngine.d.ts.map +1 -0
- package/lib/typescript/module/native/visionCameraPeer.d.ts +2 -0
- package/lib/typescript/module/native/visionCameraPeer.d.ts.map +1 -0
- package/lib/typescript/module/native/visionSurface.d.ts +41 -0
- package/lib/typescript/module/native/visionSurface.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/permissions/permissionPort.d.ts +24 -0
- package/lib/typescript/module/permissions/permissionPort.d.ts.map +1 -0
- package/lib/typescript/module/react/CameraPreview.d.ts +25 -0
- package/lib/typescript/module/react/CameraPreview.d.ts.map +1 -0
- package/lib/typescript/module/react/VisionCameraFeed.d.ts +31 -0
- package/lib/typescript/module/react/VisionCameraFeed.d.ts.map +1 -0
- package/lib/typescript/module/react/hooks.d.ts +12 -0
- package/lib/typescript/module/react/hooks.d.ts.map +1 -0
- package/lib/typescript/module/storage/artifactStore.d.ts +54 -0
- package/lib/typescript/module/storage/artifactStore.d.ts.map +1 -0
- package/lib/typescript/module/testing/conformance.d.ts +37 -0
- package/lib/typescript/module/testing/conformance.d.ts.map +1 -0
- package/lib/typescript/module/testing/index.d.ts +12 -0
- package/lib/typescript/module/testing/index.d.ts.map +1 -0
- package/lib/typescript/module/testing/testCameraAdapter.d.ts +61 -0
- package/lib/typescript/module/testing/testCameraAdapter.d.ts.map +1 -0
- package/package.json +49 -13
- package/skills/sdk-rn-camera/SKILL.md +25 -6
- package/src/config/types.ts +8 -0
- package/src/core/captureController.ts +11 -1
- package/src/core/contract.ts +109 -0
- package/src/core/controller.ts +96 -148
- package/src/core/sessionRuntime.ts +242 -0
- package/src/index.ts +7 -13
- package/src/native/cameraEngine.ts +28 -8
- package/src/native/visionSurface.ts +43 -18
- package/src/react/CameraPreview.tsx +40 -7
- package/src/react/VisionCameraFeed.tsx +61 -5
- package/src/react/hooks.ts +1 -1
- package/src/storage/artifactStore.ts +9 -2
- package/src/testing/testCameraAdapter.ts +29 -7
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CameraEngine port + default react-native-vision-camera adapter.
|
|
5
|
+
*
|
|
6
|
+
* This is the only module that knows a native camera library exists. The
|
|
7
|
+
* default adapter lazily requires it, mirroring
|
|
8
|
+
* `packages/rn-auth/src/tokens/secureStore.ts`, so unit tests that inject a
|
|
9
|
+
* fake never load native code.
|
|
10
|
+
*
|
|
11
|
+
* Preview frames never cross this boundary: `capture` returns a file reference
|
|
12
|
+
* plus metadata and nothing else (PERF-002, FR-021a).
|
|
13
|
+
*
|
|
14
|
+
* Targets the VisionCamera v5 API, where devices come from an async
|
|
15
|
+
* `CameraDeviceFactory` and capture runs through the photo output owned by the
|
|
16
|
+
* mounted preview surface (see `visionSurface`).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { loadVisionCamera } from "./visionCameraPeer.js";
|
|
20
|
+
import { CameraError } from "../diagnostics/errors.js";
|
|
21
|
+
import { createSurfaceRegistry } from "./visionSurface.js";
|
|
22
|
+
|
|
23
|
+
/** The subset of VisionCamera's `CameraDevice` this adapter reads. */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default engine. VisionCamera drives the preview through its own React
|
|
27
|
+
* component, so this adapter owns capability discovery while `CameraPreview`
|
|
28
|
+
* owns the rendered surface and the photo output that capture runs through.
|
|
29
|
+
*/
|
|
30
|
+
export function createVisionCameraEngine() {
|
|
31
|
+
// One registry per engine, so two controllers never share a surface.
|
|
32
|
+
const surfaces = createSurfaceRegistry();
|
|
33
|
+
// Loaded when the default adapter is created; a missing peer is `cameraUnavailable`.
|
|
34
|
+
const {
|
|
35
|
+
VisionCamera
|
|
36
|
+
} = loadVisionCamera();
|
|
37
|
+
|
|
38
|
+
// The factory is created once and reused: it keeps its own device list up to
|
|
39
|
+
// date, so re-creating it per call would only add latency.
|
|
40
|
+
let factory = null;
|
|
41
|
+
async function devices() {
|
|
42
|
+
factory ??= VisionCamera.createDeviceFactory();
|
|
43
|
+
return (await factory)?.cameraDevices ?? [];
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
awaitsSurfaceStart: true,
|
|
47
|
+
attachSurface: surface => surfaces.attach(surface),
|
|
48
|
+
async start() {
|
|
49
|
+
// The preview component owns session activation; the engine only needs
|
|
50
|
+
// to confirm a device exists before the surface mounts.
|
|
51
|
+
const available = await devices();
|
|
52
|
+
if (available.length === 0) {
|
|
53
|
+
throw new CameraError('cameraUnavailable', 'No camera device is available');
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
async stop() {
|
|
57
|
+
// Teardown is driven by unmounting the preview surface.
|
|
58
|
+
},
|
|
59
|
+
async capture(request) {
|
|
60
|
+
const surface = surfaces.current();
|
|
61
|
+
if (!surface) {
|
|
62
|
+
throw new CameraError('notReady', 'The default VisionCamera engine captures through the CameraPreview ' + 'surface. Mount <CameraPreview> before calling capture().');
|
|
63
|
+
}
|
|
64
|
+
return surface.capture(request);
|
|
65
|
+
},
|
|
66
|
+
async setTorch() {
|
|
67
|
+
// Torch is a preview-surface prop; the controller mirrors the state and
|
|
68
|
+
// CameraPreview applies it.
|
|
69
|
+
},
|
|
70
|
+
async getCapabilities() {
|
|
71
|
+
const available = await devices();
|
|
72
|
+
const positions = Array.from(new Set(available.map(device => device.position).filter(p => p === 'front' || p === 'back')));
|
|
73
|
+
return {
|
|
74
|
+
availablePositions: positions,
|
|
75
|
+
hasFlash: available.some(device => device.hasFlash === true),
|
|
76
|
+
hasTorch: available.some(device => device.hasTorch === true),
|
|
77
|
+
supportsOrientationChange: true,
|
|
78
|
+
hasCameraHardware: available.length > 0
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=cameraEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["loadVisionCamera","CameraError","createSurfaceRegistry","createVisionCameraEngine","surfaces","VisionCamera","factory","devices","createDeviceFactory","cameraDevices","awaitsSurfaceStart","attachSurface","surface","attach","start","available","length","stop","capture","request","current","setTorch","getCapabilities","positions","Array","from","Set","map","device","position","filter","p","availablePositions","hasFlash","some","hasTorch","supportsOrientationChange","hasCameraHardware"],"sourceRoot":"../../../src","sources":["native/cameraEngine.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,gBAAgB,QAAQ,uBAAoB;AAQrD,SAASC,WAAW,QAAQ,0BAAuB;AACnD,SAASC,qBAAqB,QAA4B,oBAAiB;;AA4B3E;;AAOA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CAAA,EAAiB;EACxD;EACA,MAAMC,QAAQ,GAAGF,qBAAqB,CAAC,CAAC;EACxC;EACA,MAAM;IAAEG;EAAa,CAAC,GAAGL,gBAAgB,CAItC,CAAC;;EAEJ;EACA;EACA,IAAIM,OAAuE,GAAG,IAAI;EAElF,eAAeC,OAAOA,CAAA,EAAyC;IAC9DD,OAAO,KAAKD,YAAY,CAACG,mBAAmB,CAAC,CAAC;IAC9C,OAAO,CAAC,MAAMF,OAAO,GAAGG,aAAa,IAAI,EAAE;EAC5C;EAEA,OAAO;IACNC,kBAAkB,EAAE,IAAI;IAExBC,aAAa,EAAEC,OAAO,IAAIR,QAAQ,CAACS,MAAM,CAACD,OAAO,CAAC;IAElD,MAAME,KAAKA,CAAA,EAAG;MACb;MACA;MACA,MAAMC,SAAS,GAAG,MAAMR,OAAO,CAAC,CAAC;MACjC,IAAIQ,SAAS,CAACC,MAAM,KAAK,CAAC,EAAE;QAC3B,MAAM,IAAIf,WAAW,CAAC,mBAAmB,EAAE,+BAA+B,CAAC;MAC5E;IACD,CAAC;IAED,MAAMgB,IAAIA,CAAA,EAAG;MACZ;IAAA,CACA;IAED,MAAMC,OAAOA,CAACC,OAAuB,EAAE;MACtC,MAAMP,OAAO,GAAGR,QAAQ,CAACgB,OAAO,CAAC,CAAC;MAClC,IAAI,CAACR,OAAO,EAAE;QACb,MAAM,IAAIX,WAAW,CACpB,UAAU,EACV,qEAAqE,GACpE,0DACF,CAAC;MACF;MACA,OAAOW,OAAO,CAACM,OAAO,CAACC,OAAO,CAAC;IAChC,CAAC;IAED,MAAME,QAAQA,CAAA,EAAG;MAChB;MACA;IAAA,CACA;IAED,MAAMC,eAAeA,CAAA,EAA+B;MACnD,MAAMP,SAAS,GAAG,MAAMR,OAAO,CAAC,CAAC;MACjC,MAAMgB,SAAS,GAAGC,KAAK,CAACC,IAAI,CAC3B,IAAIC,GAAG,CACNX,SAAS,CACPY,GAAG,CAACC,MAAM,IAAIA,MAAM,CAACC,QAAQ,CAAC,CAC9BC,MAAM,CAAEC,CAAC,IAA4BA,CAAC,KAAK,OAAO,IAAIA,CAAC,KAAK,MAAM,CACrE,CACD,CAAC;MACD,OAAO;QACNC,kBAAkB,EAAET,SAAS;QAC7BU,QAAQ,EAAElB,SAAS,CAACmB,IAAI,CAACN,MAAM,IAAIA,MAAM,CAACK,QAAQ,KAAK,IAAI,CAAC;QAC5DE,QAAQ,EAAEpB,SAAS,CAACmB,IAAI,CAACN,MAAM,IAAIA,MAAM,CAACO,QAAQ,KAAK,IAAI,CAAC;QAC5DC,yBAAyB,EAAE,IAAI;QAC/BC,iBAAiB,EAAEtB,SAAS,CAACC,MAAM,GAAG;MACvC,CAAC;IACF;EACD,CAAC;AACF","ignoreList":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* react-native-vision-camera, loaded on first use (rn-core's lazy peer). A missing
|
|
5
|
+
* or unlinked module is a `cameraUnavailable` CameraError, the code the controller
|
|
6
|
+
* already reports when the camera cannot be reached.
|
|
7
|
+
*/
|
|
8
|
+
import { lazyPeer } from '@chipmobilesdk/rn-core';
|
|
9
|
+
import { CameraError } from "../diagnostics/errors.js";
|
|
10
|
+
|
|
11
|
+
// The engine and the permission port each describe the slice they use.
|
|
12
|
+
|
|
13
|
+
const visionCamera = lazyPeer('react-native-vision-camera', () => {
|
|
14
|
+
// Inside `try`, so Metro bundles an app that leaves this optional peer out.
|
|
15
|
+
try {
|
|
16
|
+
return require('react-native-vision-camera');
|
|
17
|
+
} catch (cause) {
|
|
18
|
+
throw cause;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
export function loadVisionCamera() {
|
|
22
|
+
return visionCamera.require(peer => new CameraError('cameraUnavailable', `${peer} is not installed or its native module is not linked`));
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=visionCameraPeer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["lazyPeer","CameraError","visionCamera","require","cause","loadVisionCamera","peer"],"sourceRoot":"../../../src","sources":["native/visionCameraPeer.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA,SAASA,QAAQ,QAAQ,wBAAwB;AAEjD,SAASC,WAAW,QAAQ,0BAAuB;;AAEnD;;AAGA,MAAMC,YAAY,GAAGF,QAAQ,CAAC,4BAA4B,EAAE,MAAM;EACjE;EACA,IAAI;IACH,OAAOG,OAAO,CAAC,4BAA4B,CAAC;EAC7C,CAAC,CAAC,OAAOC,KAAK,EAAE;IACf,MAAMA,KAAK;EACZ;AACD,CAAC,CAAC;AAEF,OAAO,SAASC,gBAAgBA,CAAA,EAAS;EACxC,OAAOH,YAAY,CAACC,OAAO,CAC1BG,IAAI,IACH,IAAIL,WAAW,CACd,mBAAmB,EACnB,GAAGK,IAAI,sDACR,CACF,CAAC;AACF","ignoreList":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The port between a mounted preview surface and an engine.
|
|
5
|
+
*
|
|
6
|
+
* VisionCamera v5 owns capture through the session the `<Camera>` view
|
|
7
|
+
* configures, so the photo output only exists while a surface is mounted. The
|
|
8
|
+
* engine is created before any surface exists, so the surface attaches itself
|
|
9
|
+
* to the engine and the engine reads it back at capture time.
|
|
10
|
+
*
|
|
11
|
+
* The registry belongs to an engine instance, not to the module. A module-level
|
|
12
|
+
* slot meant two controllers — two screens in a stack — shared one, so a
|
|
13
|
+
* capture could run through the other screen's surface (CAM-20).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** What the mounted preview actually is, once the device and format are known. */
|
|
17
|
+
|
|
18
|
+
/** What a surface reports back about its own session. */
|
|
19
|
+
|
|
20
|
+
export function createSurfaceRegistry() {
|
|
21
|
+
let active = null;
|
|
22
|
+
return {
|
|
23
|
+
attach(surface) {
|
|
24
|
+
active = surface;
|
|
25
|
+
return () => {
|
|
26
|
+
if (active === surface) {
|
|
27
|
+
active = null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
current: () => active
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=visionSurface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createSurfaceRegistry","active","attach","surface","current"],"sourceRoot":"../../../src","sources":["native/visionSurface.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAKA;;AAeA;;AAcA,OAAO,SAASA,qBAAqBA,CAAA,EAAoB;EACxD,IAAIC,MAA4B,GAAG,IAAI;EACvC,OAAO;IACNC,MAAMA,CAACC,OAAO,EAAE;MACfF,MAAM,GAAGE,OAAO;MAChB,OAAO,MAAM;QACZ,IAAIF,MAAM,KAAKE,OAAO,EAAE;UACvBF,MAAM,GAAG,IAAI;QACd;MACD,CAAC;IACF,CAAC;IACDG,OAAO,EAAEA,CAAA,KAAMH;EAChB,CAAC;AACF","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PermissionPort + default react-native-vision-camera adapter (FR-004..FR-008).
|
|
5
|
+
*
|
|
6
|
+
* `getStatus` never prompts and never opens a session (FR-005); only `request`
|
|
7
|
+
* may prompt, and it refuses to re-prompt once the OS reports a permanent
|
|
8
|
+
* denial (FR-006). The default adapter lazily requires the native module so
|
|
9
|
+
* unit tests that inject a fake never load native code — same approach as
|
|
10
|
+
* `packages/rn-auth/src/tokens/secureStore.ts`.
|
|
11
|
+
*
|
|
12
|
+
* The adapter targets the VisionCamera v5 API: permissions live on the
|
|
13
|
+
* `VisionCamera` hybrid object, not on the `Camera` component (which carries no
|
|
14
|
+
* statics since v5).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { loadVisionCamera } from "../native/visionCameraPeer.js";
|
|
18
|
+
import { appLifecycle, createListenerSet } from '@chipmobilesdk/rn-core';
|
|
19
|
+
/**
|
|
20
|
+
* VisionCamera `PermissionStatus` mapped onto our seven-state contract.
|
|
21
|
+
*
|
|
22
|
+
* `not-determined` also covers "denied once, but the app may ask again" on
|
|
23
|
+
* Android, which is exactly the state where re-prompting is allowed, so it maps
|
|
24
|
+
* to `undetermined` rather than `denied`.
|
|
25
|
+
*/
|
|
26
|
+
function mapStatus(raw) {
|
|
27
|
+
switch (raw) {
|
|
28
|
+
case 'authorized':
|
|
29
|
+
return 'granted';
|
|
30
|
+
case 'not-determined':
|
|
31
|
+
return 'undetermined';
|
|
32
|
+
case 'denied':
|
|
33
|
+
return 'deniedPermanently';
|
|
34
|
+
case 'restricted':
|
|
35
|
+
return 'deniedPermanently';
|
|
36
|
+
default:
|
|
37
|
+
return 'unsupported';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function createVisionCameraPermissions() {
|
|
41
|
+
// Loaded when the default adapter is created; a missing peer is `cameraUnavailable`.
|
|
42
|
+
const {
|
|
43
|
+
VisionCamera
|
|
44
|
+
} = loadVisionCamera();
|
|
45
|
+
const listeners = createListenerSet();
|
|
46
|
+
let last = null;
|
|
47
|
+
let stopWatchingLifecycle = null;
|
|
48
|
+
function read() {
|
|
49
|
+
try {
|
|
50
|
+
return mapStatus(VisionCamera.cameraPermissionStatus);
|
|
51
|
+
} catch {
|
|
52
|
+
// The native module is unreachable; report it rather than pretending
|
|
53
|
+
// the permission is merely undetermined.
|
|
54
|
+
return 'unsupported';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function emit(state) {
|
|
58
|
+
if (state !== last) {
|
|
59
|
+
last = state;
|
|
60
|
+
listeners.notify(state);
|
|
61
|
+
}
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
async getStatus() {
|
|
66
|
+
return emit(read());
|
|
67
|
+
},
|
|
68
|
+
async request() {
|
|
69
|
+
// Never re-prompt after a permanent denial (FR-006); report the
|
|
70
|
+
// settings-required state instead so the app can route the user.
|
|
71
|
+
const current = read();
|
|
72
|
+
if (current === 'deniedPermanently') {
|
|
73
|
+
return emit(current);
|
|
74
|
+
}
|
|
75
|
+
await VisionCamera.requestCameraPermission();
|
|
76
|
+
// Re-read rather than trusting the boolean result: the status carries
|
|
77
|
+
// the richer distinction the contract needs.
|
|
78
|
+
return emit(read());
|
|
79
|
+
},
|
|
80
|
+
subscribe(listener) {
|
|
81
|
+
const remove = listeners.add(listener);
|
|
82
|
+
// Grants and revocations made in system Settings send the app through
|
|
83
|
+
// background/foreground, so re-reading on foreground is what surfaces
|
|
84
|
+
// them (FR-007). Attached only while somebody is listening.
|
|
85
|
+
// rn-core's shared lifecycle port, so this adds no native subscription of its own.
|
|
86
|
+
stopWatchingLifecycle ??= appLifecycle().subscribe(state => {
|
|
87
|
+
if (state === 'active') {
|
|
88
|
+
emit(read());
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return () => {
|
|
92
|
+
remove();
|
|
93
|
+
if (listeners.size === 0 && stopWatchingLifecycle) {
|
|
94
|
+
stopWatchingLifecycle();
|
|
95
|
+
stopWatchingLifecycle = null;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=permissionPort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["loadVisionCamera","appLifecycle","createListenerSet","mapStatus","raw","createVisionCameraPermissions","VisionCamera","listeners","last","stopWatchingLifecycle","read","cameraPermissionStatus","emit","state","notify","getStatus","request","current","requestCameraPermission","subscribe","listener","remove","add","size"],"sourceRoot":"../../../src","sources":["permissions/permissionPort.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,gBAAgB,QAAQ,+BAA4B;AAC7D,SAASC,YAAY,EAAEC,iBAAiB,QAAQ,wBAAwB;AAaxE;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,SAASA,CAACC,GAAW,EAAyB;EACtD,QAAQA,GAAG;IACV,KAAK,YAAY;MAChB,OAAO,SAAS;IACjB,KAAK,gBAAgB;MACpB,OAAO,cAAc;IACtB,KAAK,QAAQ;MACZ,OAAO,mBAAmB;IAC3B,KAAK,YAAY;MAChB,OAAO,mBAAmB;IAC3B;MACC,OAAO,aAAa;EACtB;AACD;AAEA,OAAO,SAASC,6BAA6BA,CAAA,EAAmB;EAC/D;EACA,MAAM;IAAEC;EAAa,CAAC,GAAGN,gBAAgB,CAKtC,CAAC;EACJ,MAAMO,SAAS,GAAGL,iBAAiB,CAA0B,CAAC;EAC9D,IAAIM,IAAkC,GAAG,IAAI;EAC7C,IAAIC,qBAA0C,GAAG,IAAI;EAErD,SAASC,IAAIA,CAAA,EAA0B;IACtC,IAAI;MACH,OAAOP,SAAS,CAACG,YAAY,CAACK,sBAAsB,CAAC;IACtD,CAAC,CAAC,MAAM;MACP;MACA;MACA,OAAO,aAAa;IACrB;EACD;EAEA,SAASC,IAAIA,CAACC,KAA4B,EAAyB;IAClE,IAAIA,KAAK,KAAKL,IAAI,EAAE;MACnBA,IAAI,GAAGK,KAAK;MACZN,SAAS,CAACO,MAAM,CAACD,KAAK,CAAC;IACxB;IACA,OAAOA,KAAK;EACb;EAEA,OAAO;IACN,MAAME,SAASA,CAAA,EAAG;MACjB,OAAOH,IAAI,CAACF,IAAI,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,MAAMM,OAAOA,CAAA,EAAG;MACf;MACA;MACA,MAAMC,OAAO,GAAGP,IAAI,CAAC,CAAC;MACtB,IAAIO,OAAO,KAAK,mBAAmB,EAAE;QACpC,OAAOL,IAAI,CAACK,OAAO,CAAC;MACrB;MACA,MAAMX,YAAY,CAACY,uBAAuB,CAAC,CAAC;MAC5C;MACA;MACA,OAAON,IAAI,CAACF,IAAI,CAAC,CAAC,CAAC;IACpB,CAAC;IAEDS,SAASA,CAACC,QAAQ,EAAE;MACnB,MAAMC,MAAM,GAAGd,SAAS,CAACe,GAAG,CAACF,QAAQ,CAAC;MACtC;MACA;MACA;MACA;MACAX,qBAAqB,KAAKR,YAAY,CAAC,CAAC,CAACkB,SAAS,CAACN,KAAK,IAAI;QAC3D,IAAIA,KAAK,KAAK,QAAQ,EAAE;UACvBD,IAAI,CAACF,IAAI,CAAC,CAAC,CAAC;QACb;MACD,CAAC,CAAC;MACF,OAAO,MAAM;QACZW,MAAM,CAAC,CAAC;QACR,IAAId,SAAS,CAACgB,IAAI,KAAK,CAAC,IAAId,qBAAqB,EAAE;UAClDA,qBAAqB,CAAC,CAAC;UACvBA,qBAAqB,GAAG,IAAI;QAC7B;MACD,CAAC;IACF;EACD,CAAC;AACF","ignoreList":[]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Camera preview surface (FR-009..FR-013, UX-001, UX-003).
|
|
5
|
+
*
|
|
6
|
+
* Renders the camera feed and nothing else: no shutter button, no permission
|
|
7
|
+
* copy, no error text, and no theme dependency. Consuming apps draw their own
|
|
8
|
+
* controls and overlays through `children`, positioned with the geometry
|
|
9
|
+
* reported by `onGeometryChange`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
13
|
+
import { View } from 'react-native';
|
|
14
|
+
import { useCameraState } from "./hooks.js";
|
|
15
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
16
|
+
/** The feed module failed to load; distinct from `null`, which means not requested. */
|
|
17
|
+
const FEED_UNAVAILABLE = 'unavailable';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Nominal capture dimensions, until the mounted surface reports its own.
|
|
21
|
+
*
|
|
22
|
+
* Geometry built on these was landscape on a portrait phone, so every mapped
|
|
23
|
+
* rectangle was wrong on a real device (CAM-10). `onGeometryChange` now fires
|
|
24
|
+
* again with the real numbers as soon as the preview reports them, and apps
|
|
25
|
+
* that map coordinates should wait for `state.surface` rather than the first
|
|
26
|
+
* geometry.
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_IMAGE_WIDTH = 1920;
|
|
29
|
+
const DEFAULT_IMAGE_HEIGHT = 1080;
|
|
30
|
+
export const CameraPreview = ({
|
|
31
|
+
controller,
|
|
32
|
+
resizeMode = 'cover',
|
|
33
|
+
style,
|
|
34
|
+
onGeometryChange,
|
|
35
|
+
accessibilityLabel,
|
|
36
|
+
accessibilityRole,
|
|
37
|
+
children
|
|
38
|
+
}) => {
|
|
39
|
+
const [layout, setLayout] = useState(null);
|
|
40
|
+
// Subscribed, not read once: a plain `controller.state` read only refreshed
|
|
41
|
+
// when the parent happened to re-render, so the feed could stay unmounted
|
|
42
|
+
// after the session became active (or stay mounted after it stopped).
|
|
43
|
+
const state = useCameraState(controller);
|
|
44
|
+
|
|
45
|
+
// Tell the controller a surface exists so foregrounding can restart the
|
|
46
|
+
// session, and clear it on unmount so the camera is released (FR-016). Both
|
|
47
|
+
// calls are no-ops on an idle or disposed controller.
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
controller.setSurfaceMounted(true);
|
|
50
|
+
return () => {
|
|
51
|
+
controller.setSurfaceMounted(false);
|
|
52
|
+
controller.stop().catch(() => undefined);
|
|
53
|
+
};
|
|
54
|
+
}, [controller]);
|
|
55
|
+
const attachSurface = useCallback(surface => controller.attachSurface(surface), [controller]);
|
|
56
|
+
const onSurfaceStarted = useCallback(info => controller.reportSurfaceStarted(info), [controller]);
|
|
57
|
+
const onSurfaceFailed = useCallback(code => controller.reportSurfaceFailed(code), [controller]);
|
|
58
|
+
const onLayout = useCallback(event => {
|
|
59
|
+
const {
|
|
60
|
+
width,
|
|
61
|
+
height
|
|
62
|
+
} = event.nativeEvent.layout;
|
|
63
|
+
setLayout({
|
|
64
|
+
width,
|
|
65
|
+
height
|
|
66
|
+
});
|
|
67
|
+
}, []);
|
|
68
|
+
const geometry = useMemo(() => {
|
|
69
|
+
if (!layout || layout.width === 0 || layout.height === 0) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const surface = state.surface;
|
|
73
|
+
return {
|
|
74
|
+
imageWidth: surface && surface.imageWidth > 0 ? surface.imageWidth : DEFAULT_IMAGE_WIDTH,
|
|
75
|
+
imageHeight: surface && surface.imageHeight > 0 ? surface.imageHeight : DEFAULT_IMAGE_HEIGHT,
|
|
76
|
+
previewWidth: layout.width,
|
|
77
|
+
previewHeight: layout.height,
|
|
78
|
+
resizeMode,
|
|
79
|
+
orientation: surface?.orientation ?? 0,
|
|
80
|
+
isMirrored: (surface?.cameraPosition ?? state.cameraPosition) === 'front'
|
|
81
|
+
};
|
|
82
|
+
}, [layout, resizeMode, state.cameraPosition, state.surface]);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (geometry && onGeometryChange) {
|
|
85
|
+
onGeometryChange(geometry);
|
|
86
|
+
}
|
|
87
|
+
}, [geometry, onGeometryChange]);
|
|
88
|
+
|
|
89
|
+
// Mounted while the session is coming up, running, or paused: the surface is
|
|
90
|
+
// what makes it come up at all, and unmounting it on a pause meant rebuilding
|
|
91
|
+
// the device, the session and the photo output on every resume (CAM-08, CAM-09).
|
|
92
|
+
const feedRequested = state.permission === 'granted' && (state.session === 'starting' || state.session === 'active' || state.session === 'paused');
|
|
93
|
+
|
|
94
|
+
// Lazy require so importing this package never pulls in the native module
|
|
95
|
+
// for consumers that only use the pure utilities or the test adapter.
|
|
96
|
+
const Feed = useMemo(() => {
|
|
97
|
+
if (!feedRequested) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
return require('./VisionCameraFeed').VisionCameraFeed;
|
|
102
|
+
} catch {
|
|
103
|
+
// Reported, not swallowed (CAM-21): the session fails with
|
|
104
|
+
// `cameraUnavailable` instead of showing a black preview.
|
|
105
|
+
return FEED_UNAVAILABLE;
|
|
106
|
+
}
|
|
107
|
+
}, [feedRequested]);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
if (Feed === FEED_UNAVAILABLE) {
|
|
110
|
+
controller.reportSurfaceUnavailable();
|
|
111
|
+
}
|
|
112
|
+
}, [Feed, controller]);
|
|
113
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
114
|
+
style: style,
|
|
115
|
+
onLayout: onLayout,
|
|
116
|
+
accessibilityLabel: accessibilityLabel,
|
|
117
|
+
accessibilityRole: accessibilityRole,
|
|
118
|
+
testID: "rn-camera-preview",
|
|
119
|
+
children: [Feed && Feed !== FEED_UNAVAILABLE ? /*#__PURE__*/_jsx(Feed, {
|
|
120
|
+
cameraPosition: state.cameraPosition,
|
|
121
|
+
resizeMode: resizeMode,
|
|
122
|
+
torchEnabled: state.torchEnabled,
|
|
123
|
+
isActive: state.session !== 'paused',
|
|
124
|
+
attachSurface: attachSurface,
|
|
125
|
+
onStarted: onSurfaceStarted,
|
|
126
|
+
onFailed: onSurfaceFailed
|
|
127
|
+
}) : null, children]
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=CameraPreview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","useCallback","useEffect","useMemo","useState","View","useCameraState","jsx","_jsx","jsxs","_jsxs","FEED_UNAVAILABLE","DEFAULT_IMAGE_WIDTH","DEFAULT_IMAGE_HEIGHT","CameraPreview","controller","resizeMode","style","onGeometryChange","accessibilityLabel","accessibilityRole","children","layout","setLayout","state","setSurfaceMounted","stop","catch","undefined","attachSurface","surface","onSurfaceStarted","info","reportSurfaceStarted","onSurfaceFailed","code","reportSurfaceFailed","onLayout","event","width","height","nativeEvent","geometry","imageWidth","imageHeight","previewWidth","previewHeight","orientation","isMirrored","cameraPosition","feedRequested","permission","session","Feed","require","VisionCameraFeed","reportSurfaceUnavailable","testID","torchEnabled","isActive","onStarted","onFailed"],"sourceRoot":"../../../src","sources":["react/CameraPreview.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,IAAIC,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,OAAO;AACxE,SACCC,IAAI,QAKE,cAAc;AAMrB,SAASC,cAAc,QAAQ,YAAS;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAezC;AACA,MAAMC,gBAAgB,GAAG,aAAsB;;AAE/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,mBAAmB,GAAG,IAAI;AAChC,MAAMC,oBAAoB,GAAG,IAAI;AAEjC,OAAO,MAAMC,aAA2C,GAAGA,CAAC;EAC3DC,UAAU;EACVC,UAAU,GAAG,OAAO;EACpBC,KAAK;EACLC,gBAAgB;EAChBC,kBAAkB;EAClBC,iBAAiB;EACjBC;AACD,CAAC,KAAK;EACL,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,GAAGnB,QAAQ,CAA2C,IAAI,CAAC;EACpF;EACA;EACA;EACA,MAAMoB,KAAK,GAAGlB,cAAc,CAACS,UAAU,CAAC;;EAExC;EACA;EACA;EACAb,SAAS,CAAC,MAAM;IACfa,UAAU,CAACU,iBAAiB,CAAC,IAAI,CAAC;IAClC,OAAO,MAAM;MACZV,UAAU,CAACU,iBAAiB,CAAC,KAAK,CAAC;MACnCV,UAAU,CAACW,IAAI,CAAC,CAAC,CAACC,KAAK,CAAC,MAAMC,SAAS,CAAC;IACzC,CAAC;EACF,CAAC,EAAE,CAACb,UAAU,CAAC,CAAC;EAEhB,MAAMc,aAAa,GAAG5B,WAAW,CAC/B6B,OAAsB,IAAKf,UAAU,CAACc,aAAa,CAACC,OAAO,CAAC,EAC7D,CAACf,UAAU,CACZ,CAAC;EACD,MAAMgB,gBAAgB,GAAG9B,WAAW,CAClC+B,IAAiB,IAAKjB,UAAU,CAACkB,oBAAoB,CAACD,IAAI,CAAC,EAC5D,CAACjB,UAAU,CACZ,CAAC;EACD,MAAMmB,eAAe,GAAGjC,WAAW,CACjCkC,IAAqB,IAAKpB,UAAU,CAACqB,mBAAmB,CAACD,IAAI,CAAC,EAC/D,CAACpB,UAAU,CACZ,CAAC;EAED,MAAMsB,QAAQ,GAAGpC,WAAW,CAAEqC,KAAwB,IAAK;IAC1D,MAAM;MAAEC,KAAK;MAAEC;IAAO,CAAC,GAAGF,KAAK,CAACG,WAAW,CAACnB,MAAM;IAClDC,SAAS,CAAC;MAAEgB,KAAK;MAAEC;IAAO,CAAC,CAAC;EAC7B,CAAC,EAAE,EAAE,CAAC;EAEN,MAAME,QAAQ,GAAGvC,OAAO,CAAyB,MAAM;IACtD,IAAI,CAACmB,MAAM,IAAIA,MAAM,CAACiB,KAAK,KAAK,CAAC,IAAIjB,MAAM,CAACkB,MAAM,KAAK,CAAC,EAAE;MACzD,OAAO,IAAI;IACZ;IACA,MAAMV,OAAO,GAAGN,KAAK,CAACM,OAAO;IAC7B,OAAO;MACNa,UAAU,EAAEb,OAAO,IAAIA,OAAO,CAACa,UAAU,GAAG,CAAC,GAAGb,OAAO,CAACa,UAAU,GAAG/B,mBAAmB;MACxFgC,WAAW,EAAEd,OAAO,IAAIA,OAAO,CAACc,WAAW,GAAG,CAAC,GAAGd,OAAO,CAACc,WAAW,GAAG/B,oBAAoB;MAC5FgC,YAAY,EAAEvB,MAAM,CAACiB,KAAK;MAC1BO,aAAa,EAAExB,MAAM,CAACkB,MAAM;MAC5BxB,UAAU;MACV+B,WAAW,EAAEjB,OAAO,EAAEiB,WAAW,IAAI,CAAC;MACtCC,UAAU,EAAE,CAAClB,OAAO,EAAEmB,cAAc,IAAIzB,KAAK,CAACyB,cAAc,MAAM;IACnE,CAAC;EACF,CAAC,EAAE,CAAC3B,MAAM,EAAEN,UAAU,EAAEQ,KAAK,CAACyB,cAAc,EAAEzB,KAAK,CAACM,OAAO,CAAC,CAAC;EAE7D5B,SAAS,CAAC,MAAM;IACf,IAAIwC,QAAQ,IAAIxB,gBAAgB,EAAE;MACjCA,gBAAgB,CAACwB,QAAQ,CAAC;IAC3B;EACD,CAAC,EAAE,CAACA,QAAQ,EAAExB,gBAAgB,CAAC,CAAC;;EAEhC;EACA;EACA;EACA,MAAMgC,aAAa,GAClB1B,KAAK,CAAC2B,UAAU,KAAK,SAAS,KAC7B3B,KAAK,CAAC4B,OAAO,KAAK,UAAU,IAAI5B,KAAK,CAAC4B,OAAO,KAAK,QAAQ,IAAI5B,KAAK,CAAC4B,OAAO,KAAK,QAAQ,CAAC;;EAE3F;EACA;EACA,MAAMC,IAAI,GAAGlD,OAAO,CAAC,MAAM;IAC1B,IAAI,CAAC+C,aAAa,EAAE;MACnB,OAAO,IAAI;IACZ;IACA,IAAI;MACH,OAAOI,OAAO,CAAC,oBAAoB,CAAC,CAClCC,gBAAgB;IACnB,CAAC,CAAC,MAAM;MACP;MACA;MACA,OAAO5C,gBAAgB;IACxB;EACD,CAAC,EAAE,CAACuC,aAAa,CAAC,CAAC;EAEnBhD,SAAS,CAAC,MAAM;IACf,IAAImD,IAAI,KAAK1C,gBAAgB,EAAE;MAC9BI,UAAU,CAACyC,wBAAwB,CAAC,CAAC;IACtC;EACD,CAAC,EAAE,CAACH,IAAI,EAAEtC,UAAU,CAAC,CAAC;EAEtB,oBACCL,KAAA,CAACL,IAAI;IACJY,KAAK,EAAEA,KAAM;IACboB,QAAQ,EAAEA,QAAS;IACnBlB,kBAAkB,EAAEA,kBAAmB;IACvCC,iBAAiB,EAAEA,iBAAkB;IACrCqC,MAAM,EAAC,mBAAmB;IAAApC,QAAA,GAEzBgC,IAAI,IAAIA,IAAI,KAAK1C,gBAAgB,gBACjCH,IAAA,CAAC6C,IAAI;MACJJ,cAAc,EAAEzB,KAAK,CAACyB,cAAe;MACrCjC,UAAU,EAAEA,UAAW;MACvB0C,YAAY,EAAElC,KAAK,CAACkC,YAAa;MACjCC,QAAQ,EAAEnC,KAAK,CAAC4B,OAAO,KAAK,QAAS;MACrCvB,aAAa,EAAEA,aAAc;MAC7B+B,SAAS,EAAE7B,gBAAiB;MAC5B8B,QAAQ,EAAE3B;IAAgB,CAC1B,CAAC,GACC,IAAI,EACPb,QAAQ;EAAA,CACJ,CAAC;AAET,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The live VisionCamera surface rendered inside `CameraPreview`.
|
|
5
|
+
*
|
|
6
|
+
* Split into its own module so `CameraPreview` can require it lazily: consumers
|
|
7
|
+
* that only use the pure utilities or the test adapter never load the native
|
|
8
|
+
* module, while the hooks this component needs (`useCameraDevice`,
|
|
9
|
+
* `usePhotoOutput`) can still be called normally.
|
|
10
|
+
*
|
|
11
|
+
* It also owns capture. In VisionCamera v5 the photo output belongs to the
|
|
12
|
+
* session the `<Camera>` view configures, so the output only exists while this
|
|
13
|
+
* surface is mounted; it is published to the engine through `visionSurface`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
17
|
+
import { StyleSheet } from 'react-native';
|
|
18
|
+
import { Camera, useCameraDevice, usePhotoOutput } from 'react-native-vision-camera';
|
|
19
|
+
import { known, unavailable } from "../config/types.js";
|
|
20
|
+
import { DEFAULT_QUALITY } from "../core/captureController.js";
|
|
21
|
+
import { CameraError, isCameraError } from "../diagnostics/errors.js";
|
|
22
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
23
|
+
const styles = StyleSheet.create({
|
|
24
|
+
feed: {
|
|
25
|
+
flex: 1
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const MIME_TYPES = {
|
|
29
|
+
jpeg: 'image/jpeg',
|
|
30
|
+
heic: 'image/heic',
|
|
31
|
+
dng: 'image/x-adobe-dng',
|
|
32
|
+
tiff: 'image/tiff'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** VisionCamera reports orientation as a compass value; the contract wants degrees. */
|
|
36
|
+
const ORIENTATION_DEGREES = {
|
|
37
|
+
up: 0,
|
|
38
|
+
right: 90,
|
|
39
|
+
down: 180,
|
|
40
|
+
left: 270
|
|
41
|
+
};
|
|
42
|
+
function orientationOf(orientation) {
|
|
43
|
+
const degrees = ORIENTATION_DEGREES[orientation];
|
|
44
|
+
return degrees === undefined ? unavailable() : known(degrees);
|
|
45
|
+
}
|
|
46
|
+
function toFileUri(path) {
|
|
47
|
+
return path.startsWith('file://') ? path : `file://${path}`;
|
|
48
|
+
}
|
|
49
|
+
function toFilePath(uri) {
|
|
50
|
+
return uri.startsWith('file://') ? uri.slice('file://'.length) : uri;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* What a native session error means, from the code VisionCamera reports
|
|
55
|
+
* (CAM-08). Anything unrecognised is an interruption: the session is gone, and
|
|
56
|
+
* the app may try again.
|
|
57
|
+
*/
|
|
58
|
+
function nativeSessionCode(error) {
|
|
59
|
+
const code = String(error?.code ?? '').toLowerCase();
|
|
60
|
+
if (code.includes('permission')) return 'permissionDenied';
|
|
61
|
+
if (code.includes('in-use') || code.includes('already-in-use')) return 'cameraInUse';
|
|
62
|
+
if (code.includes('unavailable') || code.includes('no-device')) return 'cameraUnavailable';
|
|
63
|
+
if (code.includes('restricted') || code.includes('blocked')) return 'cameraBlocked';
|
|
64
|
+
return 'interrupted';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Native failures carry no code and may name a file path; never surface them raw. */
|
|
68
|
+
function asCameraError(error, code) {
|
|
69
|
+
return isCameraError(error) ? error : new CameraError(code);
|
|
70
|
+
}
|
|
71
|
+
async function capturePhoto(photoOutput, request, /** The device actually mounted; the request cannot ask for another one. */
|
|
72
|
+
cameraPosition) {
|
|
73
|
+
let photo;
|
|
74
|
+
try {
|
|
75
|
+
photo = await photoOutput.capturePhoto({
|
|
76
|
+
flashMode: request.flashMode ?? 'off'
|
|
77
|
+
}, {});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw asCameraError(error, 'captureFailed');
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
// A destination makes the artifact app-owned, so it is written straight
|
|
83
|
+
// there; otherwise it goes to a temporary file (FR-026a).
|
|
84
|
+
let path;
|
|
85
|
+
if (request.destination) {
|
|
86
|
+
try {
|
|
87
|
+
await photo.saveToFileAsync(toFilePath(request.destination));
|
|
88
|
+
} catch {
|
|
89
|
+
throw new CameraError('storageFailure', 'Could not write the capture to the requested destination');
|
|
90
|
+
}
|
|
91
|
+
path = request.destination;
|
|
92
|
+
} else {
|
|
93
|
+
// VisionCamera's own temporary directory. The controller moves the file
|
|
94
|
+
// into the artifact store, when it has one, so cleanup can reach it.
|
|
95
|
+
try {
|
|
96
|
+
path = await photo.saveToTemporaryFileAsync();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
throw asCameraError(error, 'storageFailure');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
uri: toFileUri(path),
|
|
103
|
+
mimeType: MIME_TYPES[photo.containerFormat] ?? 'application/octet-stream',
|
|
104
|
+
width: photo.width,
|
|
105
|
+
height: photo.height,
|
|
106
|
+
timestamp: Date.now(),
|
|
107
|
+
// The camera that took it, not the one the request named (CAM-06).
|
|
108
|
+
cameraPosition,
|
|
109
|
+
// Ownership is decided by the controller from the original request;
|
|
110
|
+
// this value is overwritten there and is only a safe default.
|
|
111
|
+
ownership: request.destination ? 'appOwned' : 'packageTemporary',
|
|
112
|
+
// Quality is fixed on the photo output, not per capture, and nothing here
|
|
113
|
+
// resizes. Report what was actually applied rather than echo the hints.
|
|
114
|
+
effectiveQuality: DEFAULT_QUALITY,
|
|
115
|
+
effectiveMaxDimension: unavailable(),
|
|
116
|
+
// The native layer reports neither back after a save, and guessing them
|
|
117
|
+
// would be worse than saying so (FR-022).
|
|
118
|
+
fileSize: unavailable(),
|
|
119
|
+
orientation: orientationOf(photo.orientation)
|
|
120
|
+
};
|
|
121
|
+
} finally {
|
|
122
|
+
// Large native buffers must not wait for the JS GC.
|
|
123
|
+
photo.dispose();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** What the device actually is, for coordinate mapping (CAM-10). */
|
|
128
|
+
function surfaceInfoOf(device, fallback) {
|
|
129
|
+
const format = device?.formats?.[0];
|
|
130
|
+
return {
|
|
131
|
+
imageWidth: format?.photoWidth ?? format?.videoWidth ?? 0,
|
|
132
|
+
imageHeight: format?.photoHeight ?? format?.videoHeight ?? 0,
|
|
133
|
+
orientation: 0,
|
|
134
|
+
cameraPosition: device?.position === 'front' || device?.position === 'back' ? device.position : fallback
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The slice of VisionCamera's `CameraDeviceFormat` the geometry needs. */
|
|
139
|
+
|
|
140
|
+
export const VisionCameraFeed = ({
|
|
141
|
+
cameraPosition,
|
|
142
|
+
resizeMode,
|
|
143
|
+
torchEnabled,
|
|
144
|
+
isActive,
|
|
145
|
+
attachSurface,
|
|
146
|
+
onStarted,
|
|
147
|
+
onFailed
|
|
148
|
+
}) => {
|
|
149
|
+
const device = useCameraDevice(cameraPosition);
|
|
150
|
+
const photoOutput = usePhotoOutput({
|
|
151
|
+
quality: DEFAULT_QUALITY
|
|
152
|
+
});
|
|
153
|
+
const surface = useMemo(() => ({
|
|
154
|
+
capture: request => capturePhoto(photoOutput, request, cameraPosition)
|
|
155
|
+
}), [photoOutput, cameraPosition]);
|
|
156
|
+
useEffect(() => attachSurface(surface), [attachSurface, surface]);
|
|
157
|
+
|
|
158
|
+
// VisionCamera applies `torchMode` on mount, before the camera is active, and
|
|
159
|
+
// leaves the native "Camera is not active" rejection unhandled — one red
|
|
160
|
+
// error per session start. Leave the prop unset until the torch is first
|
|
161
|
+
// turned on; after that 'off' must be sent explicitly to turn it back off.
|
|
162
|
+
const [torchTouched, setTorchTouched] = useState(false);
|
|
163
|
+
if (torchEnabled && !torchTouched) {
|
|
164
|
+
setTorchTouched(true);
|
|
165
|
+
}
|
|
166
|
+
const torchMode = torchEnabled ? 'on' : torchTouched ? 'off' : undefined;
|
|
167
|
+
if (!device) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
return /*#__PURE__*/_jsx(Camera, {
|
|
171
|
+
style: styles.feed,
|
|
172
|
+
device: device,
|
|
173
|
+
outputs: [photoOutput]
|
|
174
|
+
// Mounted through `paused` and `starting`: unmounting the view tore the
|
|
175
|
+
// session down and rebuilding it cost the first-frame budget (CAM-09).
|
|
176
|
+
,
|
|
177
|
+
isActive: isActive
|
|
178
|
+
// The session is active when frames arrive, not when it was asked for.
|
|
179
|
+
,
|
|
180
|
+
onStarted: () => onStarted(surfaceInfoOf(device, cameraPosition)),
|
|
181
|
+
onError: error => onFailed(nativeSessionCode(error)),
|
|
182
|
+
resizeMode: resizeMode
|
|
183
|
+
// Left unset until the app first touches the torch, so the device's own default stands.
|
|
184
|
+
,
|
|
185
|
+
...(torchMode === undefined ? {} : {
|
|
186
|
+
torchMode
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=VisionCameraFeed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","useEffect","useMemo","useState","StyleSheet","Camera","useCameraDevice","usePhotoOutput","known","unavailable","DEFAULT_QUALITY","CameraError","isCameraError","jsx","_jsx","styles","create","feed","flex","MIME_TYPES","jpeg","heic","dng","tiff","ORIENTATION_DEGREES","up","right","down","left","orientationOf","orientation","degrees","undefined","toFileUri","path","startsWith","toFilePath","uri","slice","length","nativeSessionCode","error","code","String","toLowerCase","includes","asCameraError","capturePhoto","photoOutput","request","cameraPosition","photo","flashMode","destination","saveToFileAsync","saveToTemporaryFileAsync","mimeType","containerFormat","width","height","timestamp","Date","now","ownership","effectiveQuality","effectiveMaxDimension","fileSize","dispose","surfaceInfoOf","device","fallback","format","formats","imageWidth","photoWidth","videoWidth","imageHeight","photoHeight","videoHeight","position","VisionCameraFeed","resizeMode","torchEnabled","isActive","attachSurface","onStarted","onFailed","quality","surface","capture","torchTouched","setTorchTouched","torchMode","style","outputs","onError"],"sourceRoot":"../../../src","sources":["react/VisionCameraFeed.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* React bindings for the camera controller.
|
|
5
|
+
*
|
|
6
|
+
* `useCameraController` owns the controller lifetime and disposes it on
|
|
7
|
+
* unmount, which is what guarantees the camera is released when a screen goes
|
|
8
|
+
* away (FR-016).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
12
|
+
import { createCameraController } from "../core/controller.js";
|
|
13
|
+
export function useCameraController(options) {
|
|
14
|
+
// Options usually arrive as a fresh object literal each render; capturing
|
|
15
|
+
// them in a ref keeps the controller stable for the component's lifetime.
|
|
16
|
+
const optionsRef = useRef(options);
|
|
17
|
+
// Construction has no side effects (the controller subscribes on first use),
|
|
18
|
+
// so a controller React builds and discards here costs nothing.
|
|
19
|
+
const [controller, setController] = useState(() => createCameraController(optionsRef.current));
|
|
20
|
+
const disposedRef = useRef(new WeakSet());
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
// StrictMode, and React's planned offscreen reuse, run the cleanup and then
|
|
23
|
+
// this effect again on the same component. Disposal is final, so the old
|
|
24
|
+
// controller cannot be revived; replace it. Before this, the screen kept a
|
|
25
|
+
// dead controller after the remount and the camera never started.
|
|
26
|
+
const disposed = disposedRef.current;
|
|
27
|
+
if (disposed.has(controller)) {
|
|
28
|
+
setController(createCameraController(optionsRef.current));
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return () => {
|
|
32
|
+
disposed.add(controller);
|
|
33
|
+
controller.dispose().catch(() => undefined);
|
|
34
|
+
};
|
|
35
|
+
}, [controller]);
|
|
36
|
+
return controller;
|
|
37
|
+
}
|
|
38
|
+
export function useCameraState(controller) {
|
|
39
|
+
// `subscribe` is a property of the controller object, so it is stable for the
|
|
40
|
+
// controller's lifetime, which is what useSyncExternalStore requires. The
|
|
41
|
+
// store read is tear-free, unlike the previous useState copy, which could
|
|
42
|
+
// miss a change made between render and effect.
|
|
43
|
+
return useSyncExternalStore(controller.subscribe, () => controller.state, () => controller.state);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=hooks.js.map
|