@chipmobilesdk/rn-camera 0.3.0 → 0.4.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 +22 -0
- package/README.md +36 -7
- package/package.json +1 -1
- package/skills/sdk-rn-camera/SKILL.md +19 -3
- 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 +3 -1
- 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
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type FileSystemBridge,
|
|
28
28
|
} from '../storage/artifactStore';
|
|
29
29
|
import type { AppStatus } from '../core/controller';
|
|
30
|
+
import { clampQuality } from '../core/captureController';
|
|
30
31
|
|
|
31
32
|
export interface MemoryFileSystem extends FileSystemBridge {
|
|
32
33
|
/** Seeds a file with an explicit creation time, for time-to-live tests. */
|
|
@@ -98,8 +99,15 @@ export interface TestCameraHandles {
|
|
|
98
99
|
setAppState(status: AppStatus): void;
|
|
99
100
|
/** Simulates a Settings-level permission change while running (FR-007). */
|
|
100
101
|
setPermission(state: CameraPermissionState): void;
|
|
101
|
-
/**
|
|
102
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Simulates the OS taking the camera away: an incoming call, another app,
|
|
104
|
+
* a disconnected device. A live session tears down with that code, exactly
|
|
105
|
+
* as the device adapter reports it (CAM-19), and the next start fails until
|
|
106
|
+
* `setStartError(null)` clears it.
|
|
107
|
+
*/
|
|
108
|
+
emitInterruption(code?: CameraErrorCode): void;
|
|
109
|
+
/** Simulates the preview failing to produce frames. */
|
|
110
|
+
emitPermissionRequestFailure(enabled: boolean): void;
|
|
103
111
|
setCaptureError(code: CameraErrorCode | null): void;
|
|
104
112
|
setStartError(code: CameraErrorCode | null): void;
|
|
105
113
|
/** How many times `request` showed the system prompt. */
|
|
@@ -133,12 +141,18 @@ export function createTestCamera(config: TestCameraConfig = {}): TestCameraHandl
|
|
|
133
141
|
// identically here and on a device.
|
|
134
142
|
const permissionListeners = createListenerSet<[CameraPermissionState]>();
|
|
135
143
|
const appStateListeners = createListenerSet<[AppStatus]>();
|
|
144
|
+
const interruptionListeners = createListenerSet<[CameraErrorCode]>();
|
|
145
|
+
let requestThrows = false;
|
|
136
146
|
|
|
137
147
|
const permissions: PermissionPort = {
|
|
138
148
|
async getStatus() {
|
|
139
149
|
return permission;
|
|
140
150
|
},
|
|
141
151
|
async request() {
|
|
152
|
+
if (requestThrows) {
|
|
153
|
+
// What a native module that cannot be reached does.
|
|
154
|
+
throw new Error('permission request failed');
|
|
155
|
+
}
|
|
142
156
|
// Mirrors the real adapter: never re-prompt after a permanent denial.
|
|
143
157
|
if (permission === 'deniedPermanently') {
|
|
144
158
|
return permission;
|
|
@@ -154,6 +168,8 @@ export function createTestCamera(config: TestCameraConfig = {}): TestCameraHandl
|
|
|
154
168
|
};
|
|
155
169
|
|
|
156
170
|
const engine: CameraEngine = {
|
|
171
|
+
subscribeToInterruptions: listener => interruptionListeners.add(listener),
|
|
172
|
+
|
|
157
173
|
async start() {
|
|
158
174
|
starts += 1;
|
|
159
175
|
if (startError) {
|
|
@@ -186,7 +202,8 @@ export function createTestCamera(config: TestCameraConfig = {}): TestCameraHandl
|
|
|
186
202
|
timestamp: Date.now(),
|
|
187
203
|
cameraPosition: position,
|
|
188
204
|
ownership: request.destination ? 'appOwned' : 'packageTemporary',
|
|
189
|
-
|
|
205
|
+
// Clamped the way the real path clamps it, not a second copy of 0.9.
|
|
206
|
+
effectiveQuality: clampQuality(request.quality),
|
|
190
207
|
effectiveMaxDimension:
|
|
191
208
|
request.maxDimension === undefined ? unavailable() : known(request.maxDimension),
|
|
192
209
|
fileSize: known(1024),
|
|
@@ -223,10 +240,15 @@ export function createTestCamera(config: TestCameraConfig = {}): TestCameraHandl
|
|
|
223
240
|
permissionListeners.notify(state);
|
|
224
241
|
},
|
|
225
242
|
|
|
226
|
-
emitInterruption() {
|
|
227
|
-
//
|
|
228
|
-
// start attempt fails until the app clears it.
|
|
229
|
-
startError =
|
|
243
|
+
emitInterruption(code: CameraErrorCode = 'interrupted') {
|
|
244
|
+
// The OS took the camera away. A live session hears about it, and the
|
|
245
|
+
// next start attempt fails until the app clears it.
|
|
246
|
+
startError = code;
|
|
247
|
+
interruptionListeners.notify(code);
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
emitPermissionRequestFailure(enabled) {
|
|
251
|
+
requestThrows = enabled;
|
|
230
252
|
},
|
|
231
253
|
|
|
232
254
|
setCaptureError(code) {
|