@symbiote-native/camera 0.0.0-stage → 0.0.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.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -2
  3. package/build/angular/camera-view/index.d.ts +43 -0
  4. package/build/angular/camera-view/index.js +101 -0
  5. package/build/angular/index.d.ts +3 -0
  6. package/build/angular/index.js +4 -0
  7. package/build/angular/services/camera-permissions.service.d.ts +8 -0
  8. package/build/angular/services/camera-permissions.service.js +82 -0
  9. package/build/core/android-barcode.d.ts +50 -0
  10. package/build/core/android-barcode.js +2 -0
  11. package/build/core/camera-api.d.ts +35 -0
  12. package/build/core/camera-api.js +68 -0
  13. package/build/core/camera-view-events.d.ts +3 -0
  14. package/build/core/camera-view-events.js +60 -0
  15. package/build/core/camera-view-handle.d.ts +26 -0
  16. package/build/core/camera-view-handle.js +40 -0
  17. package/build/core/camera-view.d.ts +12 -0
  18. package/build/core/camera-view.js +38 -0
  19. package/build/core/constants.d.ts +6 -0
  20. package/build/core/constants.js +7 -0
  21. package/build/core/index.d.ts +9 -0
  22. package/build/core/index.js +5 -0
  23. package/build/core/native-module.d.ts +28 -0
  24. package/build/core/native-module.js +3 -0
  25. package/build/core/picture-options.d.ts +9 -0
  26. package/build/core/picture-options.js +29 -0
  27. package/build/core/picture-ref.d.ts +2 -0
  28. package/build/core/picture-ref.js +3 -0
  29. package/build/core/props.d.ts +4 -0
  30. package/build/core/props.js +26 -0
  31. package/build/core/types.d.ts +170 -0
  32. package/build/core/types.js +1 -0
  33. package/build/react/camera-view.d.ts +8 -0
  34. package/build/react/camera-view.js +6 -0
  35. package/build/react/index.d.ts +5 -0
  36. package/build/react/index.js +4 -0
  37. package/build/react/use-camera-permissions.d.ts +9 -0
  38. package/build/react/use-camera-permissions.js +5 -0
  39. package/build/solid/camera-view.d.ts +33 -0
  40. package/build/solid/camera-view.js +4 -0
  41. package/build/solid/index.d.ts +4 -0
  42. package/build/solid/index.js +4 -0
  43. package/build/solid/use-camera-permissions.d.ts +10 -0
  44. package/build/solid/use-camera-permissions.js +5 -0
  45. package/build/svelte/camera-view.svelte +32 -0
  46. package/build/svelte/camera-view.svelte.d.ts +22 -0
  47. package/build/svelte/index.d.ts +4 -0
  48. package/build/svelte/index.js +4 -0
  49. package/build/svelte/use-camera-permissions.svelte.d.ts +9 -0
  50. package/build/svelte/use-camera-permissions.svelte.js +5 -0
  51. package/build/vue/camera-view.d.ts +2 -0
  52. package/build/vue/camera-view.js +4 -0
  53. package/build/vue/index.d.ts +4 -0
  54. package/build/vue/index.js +4 -0
  55. package/build/vue/use-camera-permissions.d.ts +10 -0
  56. package/build/vue/use-camera-permissions.js +5 -0
  57. package/build-ngc/angular/camera-view/index.d.ts +46 -0
  58. package/build-ngc/angular/camera-view/index.js +60 -0
  59. package/build-ngc/angular/camera-view/index.js.map +1 -0
  60. package/build-ngc/angular/index.d.ts +3 -0
  61. package/build-ngc/angular/index.js +5 -0
  62. package/build-ngc/angular/index.js.map +1 -0
  63. package/build-ngc/angular/services/camera-permissions.service.d.ts +13 -0
  64. package/build-ngc/angular/services/camera-permissions.service.js +28 -0
  65. package/build-ngc/angular/services/camera-permissions.service.js.map +1 -0
  66. package/build-ngc/core/android-barcode.d.ts +50 -0
  67. package/build-ngc/core/android-barcode.js +3 -0
  68. package/build-ngc/core/android-barcode.js.map +1 -0
  69. package/build-ngc/core/camera-api.d.ts +35 -0
  70. package/build-ngc/core/camera-api.js +69 -0
  71. package/build-ngc/core/camera-api.js.map +1 -0
  72. package/build-ngc/core/camera-view-events.d.ts +3 -0
  73. package/build-ngc/core/camera-view-events.js +61 -0
  74. package/build-ngc/core/camera-view-events.js.map +1 -0
  75. package/build-ngc/core/camera-view-handle.d.ts +26 -0
  76. package/build-ngc/core/camera-view-handle.js +41 -0
  77. package/build-ngc/core/camera-view-handle.js.map +1 -0
  78. package/build-ngc/core/camera-view.d.ts +12 -0
  79. package/build-ngc/core/camera-view.js +39 -0
  80. package/build-ngc/core/camera-view.js.map +1 -0
  81. package/build-ngc/core/constants.d.ts +6 -0
  82. package/build-ngc/core/constants.js +8 -0
  83. package/build-ngc/core/constants.js.map +1 -0
  84. package/build-ngc/core/index.d.ts +9 -0
  85. package/build-ngc/core/index.js +6 -0
  86. package/build-ngc/core/index.js.map +1 -0
  87. package/build-ngc/core/native-module.d.ts +28 -0
  88. package/build-ngc/core/native-module.js +4 -0
  89. package/build-ngc/core/native-module.js.map +1 -0
  90. package/build-ngc/core/picture-options.d.ts +9 -0
  91. package/build-ngc/core/picture-options.js +30 -0
  92. package/build-ngc/core/picture-options.js.map +1 -0
  93. package/build-ngc/core/picture-ref.d.ts +2 -0
  94. package/build-ngc/core/picture-ref.js +4 -0
  95. package/build-ngc/core/picture-ref.js.map +1 -0
  96. package/build-ngc/core/props.d.ts +4 -0
  97. package/build-ngc/core/props.js +27 -0
  98. package/build-ngc/core/props.js.map +1 -0
  99. package/build-ngc/core/types.d.ts +170 -0
  100. package/build-ngc/core/types.js +2 -0
  101. package/build-ngc/core/types.js.map +1 -0
  102. package/native-link.json +22 -0
  103. package/package.json +144 -3
  104. package/src/angular/camera-view/index.ts +95 -0
  105. package/src/angular/index.ts +8 -0
  106. package/src/angular/services/camera-permissions.service.ts +28 -0
  107. package/src/core/android-barcode.ts +48 -0
  108. package/src/core/camera-api.ts +99 -0
  109. package/src/core/camera-view-events.ts +103 -0
  110. package/src/core/camera-view-handle.ts +93 -0
  111. package/src/core/camera-view.ts +56 -0
  112. package/src/core/constants.ts +11 -0
  113. package/src/core/index.ts +57 -0
  114. package/src/core/native-module.ts +57 -0
  115. package/src/core/picture-options.ts +46 -0
  116. package/src/core/picture-ref.ts +4 -0
  117. package/src/core/props.ts +31 -0
  118. package/src/core/types.ts +223 -0
  119. package/src/react/camera-view.ts +17 -0
  120. package/src/react/index.ts +7 -0
  121. package/src/react/use-camera-permissions.ts +21 -0
  122. package/src/solid/camera-view.ts +9 -0
  123. package/src/solid/index.ts +6 -0
  124. package/src/solid/use-camera-permissions.ts +22 -0
  125. package/src/svelte/camera-view.svelte +32 -0
  126. package/src/svelte/index.ts +9 -0
  127. package/src/svelte/svelte-ambient.d.ts +3 -0
  128. package/src/svelte/svelte-compile.test-helper.ts +6 -0
  129. package/src/svelte/use-camera-permissions.svelte.ts +21 -0
  130. package/src/vue/camera-view.ts +5 -0
  131. package/src/vue/index.ts +6 -0
  132. package/src/vue/use-camera-permissions.ts +22 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,57 @@
1
- # Temporary Holding Version
1
+ # @symbiote-native/camera
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [`expo-camera`](https://docs.expo.dev/versions/latest/sdk/camera/) for **every**
4
+ [SymbioteNative](../../README.md) adapter: React, Vue, Svelte, Solid and Angular. The camera
5
+ preview, pictures, video recording and barcode scanning.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npx @symbiote-native/cli new my-app --camera # new app
11
+ npx @symbiote-native/cli add --camera # existing app
12
+ ```
13
+
14
+ Manual: `npm install @symbiote-native/camera`, then wire `expo-modules-autolinking` once per app.
15
+ Never install `expo-camera` or the `expo` meta-package yourself.
16
+
17
+ ## Usage
18
+
19
+ ```tsx
20
+ import { CameraView, useCameraPermissions, type ICameraViewHandle } from '@symbiote-native/camera/react';
21
+
22
+ const [permission, requestPermission] = useCameraPermissions();
23
+ const ref = useRef<ICameraViewHandle>(null);
24
+
25
+ <CameraView ref={ref} style={{ flex: 1 }} facing="back" onBarcodeScanned={scan => console.log(scan.data)} />;
26
+
27
+ const picture = await ref.current?.takePictureAsync({ quality: 0.8 });
28
+ ```
29
+
30
+ The view takes the props of upstream's `CameraView` plus the View surface. The handle has
31
+ `takePictureAsync`, `recordAsync`, `toggleRecordingAsync`, `stopRecording`, `pausePreview`,
32
+ `resumePreview`, `getAvailablePictureSizesAsync`, `getAvailableLensesAsync` and
33
+ `getSupportedFeatures`. A `ref` carries it in React and Solid (`ref={fn}`), the exposed members of
34
+ a template ref in Vue, `bind:this` in Svelte and `@ViewChild` in Angular.
35
+
36
+ The static members of upstream's `CameraView` are functions of the package: `isCameraAvailableAsync`
37
+ (was `CameraView.isAvailableAsync`), `getAvailableVideoCodecsAsync`, `launchScanner`,
38
+ `dismissScanner`, `onModernBarcodeScanned` and `isModernBarcodeScannerAvailable()`. `Camera`,
39
+ `scanFromURLAsync` and `PictureRef` keep their names. The permission hooks are
40
+ `useCameraPermissions` and `useMicrophonePermissions`, in Angular `CameraPermissionsService` and
41
+ `MicrophonePermissionsService`.
42
+
43
+ ## App setup
44
+
45
+ The package declares what `expo-camera`'s config plugin adds, and the `expo-modules-link`
46
+ postinstall writes it into the app:
47
+
48
+ | File | Entry |
49
+ | -------------------- | -------------------------------------------------------------- |
50
+ | `Info.plist` | `NSCameraUsageDescription`, `NSMicrophoneUsageDescription` |
51
+ | `AndroidManifest.xml`| `CAMERA`, `RECORD_AUDIO` |
52
+
53
+ ## Not ported
54
+
55
+ The web build (`.web` files, the browser barcode detector) and the plugin's
56
+ `barcodeScannerEnabled: false` switch, which only trims the native binary size. The only upstream
57
+ test covers the web manager, the suites here are written against the native behavior.
@@ -0,0 +1,43 @@
1
+ import { NativeViewBase } from '@symbiote-native/angular';
2
+ import type { IDescriptor } from '@symbiote-native/components';
3
+ import type { ICameraViewHandle, ICameraViewProps } from '../../core';
4
+ /** Angular twin of `expo-camera`'s `CameraView` */
5
+ export declare class CameraView extends NativeViewBase {
6
+ facing?: ICameraViewProps['facing'];
7
+ flash?: ICameraViewProps['flash'];
8
+ zoom?: ICameraViewProps['zoom'];
9
+ mode?: ICameraViewProps['mode'];
10
+ mute?: ICameraViewProps['mute'];
11
+ mirror?: ICameraViewProps['mirror'];
12
+ autofocus?: ICameraViewProps['autofocus'];
13
+ active?: ICameraViewProps['active'];
14
+ videoQuality?: ICameraViewProps['videoQuality'];
15
+ videoBitrate?: ICameraViewProps['videoBitrate'];
16
+ animateShutter?: ICameraViewProps['animateShutter'];
17
+ pictureSize?: ICameraViewProps['pictureSize'];
18
+ selectedLens?: ICameraViewProps['selectedLens'];
19
+ enableTorch?: ICameraViewProps['enableTorch'];
20
+ videoStabilizationMode?: ICameraViewProps['videoStabilizationMode'];
21
+ barcodeScannerSettings?: ICameraViewProps['barcodeScannerSettings'];
22
+ responsiveOrientationWhenOrientationLocked?: ICameraViewProps['responsiveOrientationWhenOrientationLocked'];
23
+ ratio?: ICameraViewProps['ratio'];
24
+ onCameraReady?: ICameraViewProps['onCameraReady'];
25
+ onMountError?: ICameraViewProps['onMountError'];
26
+ onBarcodeScanned?: ICameraViewProps['onBarcodeScanned'];
27
+ onResponsiveOrientationChanged?: ICameraViewProps['onResponsiveOrientationChanged'];
28
+ onAvailableLensesChanged?: ICameraViewProps['onAvailableLensesChanged'];
29
+ private readonly view;
30
+ readonly takePictureAsync: ICameraViewHandle['takePictureAsync'];
31
+ readonly recordAsync: ICameraViewHandle['recordAsync'];
32
+ readonly toggleRecordingAsync: ICameraViewHandle['toggleRecordingAsync'];
33
+ readonly stopRecording: ICameraViewHandle['stopRecording'];
34
+ readonly pausePreview: ICameraViewHandle['pausePreview'];
35
+ readonly resumePreview: ICameraViewHandle['resumePreview'];
36
+ readonly getAvailablePictureSizesAsync: ICameraViewHandle['getAvailablePictureSizesAsync'];
37
+ readonly getAvailableLensesAsync: ICameraViewHandle['getAvailableLensesAsync'];
38
+ readonly getSupportedFeatures: ICameraViewHandle['getSupportedFeatures'];
39
+ readonly getHostNode: ICameraViewHandle['getHostNode'];
40
+ constructor();
41
+ protected readonly propNames: readonly ["facing", "flash", "zoom", "mode", "mute", "mirror", "autofocus", "active", "videoQuality", "videoBitrate", "animateShutter", "pictureSize", "selectedLens", "enableTorch", "videoStabilizationMode", "barcodeScannerSettings", "responsiveOrientationWhenOrientationLocked", "ratio", "onCameraReady", "onMountError", "onBarcodeScanned", "onResponsiveOrientationChanged", "onAvailableLensesChanged"];
42
+ protected renderView(props: object): IDescriptor | null;
43
+ }
@@ -0,0 +1,101 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
36
+ import { DescriptorOutlet, NativeViewBase } from '@symbiote-native/angular';
37
+ import { createCameraView } from '../../core/index.js';
38
+ // One list is the inputs of the component and the props it hands to the native view
39
+ const CAMERA_VIEW_INPUTS = [
40
+ 'facing',
41
+ 'flash',
42
+ 'zoom',
43
+ 'mode',
44
+ 'mute',
45
+ 'mirror',
46
+ 'autofocus',
47
+ 'active',
48
+ 'videoQuality',
49
+ 'videoBitrate',
50
+ 'animateShutter',
51
+ 'pictureSize',
52
+ 'selectedLens',
53
+ 'enableTorch',
54
+ 'videoStabilizationMode',
55
+ 'barcodeScannerSettings',
56
+ 'responsiveOrientationWhenOrientationLocked',
57
+ 'ratio',
58
+ 'onCameraReady',
59
+ 'onMountError',
60
+ 'onBarcodeScanned',
61
+ 'onResponsiveOrientationChanged',
62
+ 'onAvailableLensesChanged',
63
+ ];
64
+ /** Angular twin of `expo-camera`'s `CameraView` */
65
+ let CameraView = (() => {
66
+ let _classDecorators = [Component({
67
+ selector: 'CameraView',
68
+ standalone: true,
69
+ imports: [DescriptorOutlet],
70
+ inputs: [...CAMERA_VIEW_INPUTS],
71
+ changeDetection: ChangeDetectionStrategy.OnPush,
72
+ template: `@if (descriptor; as node) {
73
+ <symbiote-descriptor-outlet [node]="node" />
74
+ }`,
75
+ })];
76
+ let _classDescriptor;
77
+ let _classExtraInitializers = [];
78
+ let _classThis;
79
+ let _classSuper = NativeViewBase;
80
+ var CameraView = class extends _classSuper {
81
+ static { _classThis = this; }
82
+ static {
83
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
84
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
85
+ CameraView = _classThis = _classDescriptor.value;
86
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
87
+ __runInitializers(_classThis, _classExtraInitializers);
88
+ }
89
+ view = createCameraView(() => this.hostNode());
90
+ constructor() {
91
+ super();
92
+ Object.assign(this, this.view.handle);
93
+ }
94
+ propNames = CAMERA_VIEW_INPUTS;
95
+ renderView(props) {
96
+ return this.view.render(props);
97
+ }
98
+ };
99
+ return CameraView = _classThis;
100
+ })();
101
+ export { CameraView };
@@ -0,0 +1,3 @@
1
+ export { CameraView } from './camera-view';
2
+ export { CameraPermissionsService, MicrophonePermissionsService, } from './services/camera-permissions.service';
3
+ export * from '../core';
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/camera/angular: камера, съёмка, запись видео и сканирование кодов на общем ядре
2
+ export { CameraView } from './camera-view/index.js';
3
+ export { CameraPermissionsService, MicrophonePermissionsService, } from './services/camera-permissions.service.js';
4
+ export * from '../core/index.js';
@@ -0,0 +1,8 @@
1
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
2
+ import type { PermissionResponse } from 'expo-modules-core';
3
+ export declare class CameraPermissionsService extends PermissionsServiceBase<PermissionResponse> {
4
+ constructor();
5
+ }
6
+ export declare class MicrophonePermissionsService extends PermissionsServiceBase<PermissionResponse> {
7
+ constructor();
8
+ }
@@ -0,0 +1,82 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { Injectable } from '@angular/core';
36
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
37
+ import { cameraPermissionMethods, microphonePermissionMethods, } from '../../core/camera-api.js';
38
+ // Angular twin of `../../react`'s camera permission hooks
39
+ let CameraPermissionsService = (() => {
40
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
41
+ let _classDescriptor;
42
+ let _classExtraInitializers = [];
43
+ let _classThis;
44
+ let _classSuper = PermissionsServiceBase;
45
+ var CameraPermissionsService = class extends _classSuper {
46
+ static { _classThis = this; }
47
+ static {
48
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
49
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
50
+ CameraPermissionsService = _classThis = _classDescriptor.value;
51
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
52
+ __runInitializers(_classThis, _classExtraInitializers);
53
+ }
54
+ constructor() {
55
+ super(cameraPermissionMethods.getMethod, cameraPermissionMethods.requestMethod);
56
+ }
57
+ };
58
+ return CameraPermissionsService = _classThis;
59
+ })();
60
+ export { CameraPermissionsService };
61
+ let MicrophonePermissionsService = (() => {
62
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
63
+ let _classDescriptor;
64
+ let _classExtraInitializers = [];
65
+ let _classThis;
66
+ let _classSuper = PermissionsServiceBase;
67
+ var MicrophonePermissionsService = class extends _classSuper {
68
+ static { _classThis = this; }
69
+ static {
70
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
71
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
72
+ MicrophonePermissionsService = _classThis = _classDescriptor.value;
73
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
74
+ __runInitializers(_classThis, _classExtraInitializers);
75
+ }
76
+ constructor() {
77
+ super(microphonePermissionMethods.getMethod, microphonePermissionMethods.requestMethod);
78
+ }
79
+ };
80
+ return MicrophonePermissionsService = _classThis;
81
+ })();
82
+ export { MicrophonePermissionsService };
@@ -0,0 +1,50 @@
1
+ type IAndroidBarcodeKind<TType extends string> = {
2
+ type: TType;
3
+ };
4
+ export type IAndroidBarcode = (IAndroidBarcodeKind<'contactInfo'> & {
5
+ firstName?: string;
6
+ middleName?: string;
7
+ lastName?: string;
8
+ title?: string;
9
+ organization?: string;
10
+ email?: string;
11
+ phone?: string;
12
+ url?: string;
13
+ address?: string;
14
+ }) | (IAndroidBarcodeKind<'geoPoint'> & {
15
+ lat: string;
16
+ lng: string;
17
+ }) | (IAndroidBarcodeKind<'sms'> & {
18
+ phoneNumber?: string;
19
+ message?: string;
20
+ }) | (IAndroidBarcodeKind<'url'> & {
21
+ url?: string;
22
+ }) | (IAndroidBarcodeKind<'calendarEvent'> & {
23
+ summary?: string;
24
+ description?: string;
25
+ location?: string;
26
+ start?: string;
27
+ end?: string;
28
+ }) | (IAndroidBarcodeKind<'driverLicense'> & {
29
+ firstName?: string;
30
+ middleName?: string;
31
+ lastName?: string;
32
+ licenseNumber?: string;
33
+ expiryDate?: string;
34
+ issueDate?: string;
35
+ addressStreet?: string;
36
+ addressCity?: string;
37
+ addressState?: string;
38
+ }) | (IAndroidBarcodeKind<'email'> & {
39
+ address?: string;
40
+ subject?: string;
41
+ body?: string;
42
+ }) | (IAndroidBarcodeKind<'phone'> & {
43
+ number?: string;
44
+ phoneNumberType?: string;
45
+ }) | (IAndroidBarcodeKind<'wifi'> & {
46
+ ssid?: string;
47
+ password?: string;
48
+ encryptionType?: string;
49
+ });
50
+ export {};
@@ -0,0 +1,2 @@
1
+ // What Google's code scanner adds to a result, Android only
2
+ export {};
@@ -0,0 +1,35 @@
1
+ import type { EventSubscription, PermissionResponse } from 'expo-modules-core';
2
+ import type { ICameraBarcodeScanningResult, ICameraBarcodeType, ICameraScanningOptions, ICameraScanningResult, ICameraVideoCodec } from './types';
3
+ /** Whether the device has a camera, the permissions are a separate question */
4
+ export declare function isCameraAvailableAsync(): Promise<boolean>;
5
+ /** iOS only */
6
+ export declare function getAvailableVideoCodecsAsync(): Promise<ICameraVideoCodec[]>;
7
+ /** Whether the device has `DataScannerViewController` (iOS 16+) or Google's code scanner */
8
+ export declare function isModernBarcodeScannerAvailable(): boolean;
9
+ /** A device without the modern scanner does nothing here */
10
+ export declare function launchScanner(options?: ICameraScanningOptions): Promise<void>;
11
+ /** Dismisses the scanner on iOS, on Android it closes itself after a scan */
12
+ export declare function dismissScanner(): Promise<void>;
13
+ export declare function onModernBarcodeScanned(listener: (event: ICameraScanningResult) => void): EventSubscription;
14
+ /** Only QR codes are scanned on iOS */
15
+ export declare function scanFromURLAsync(url: string, barcodeTypes?: ICameraBarcodeType[]): Promise<ICameraBarcodeScanningResult[]>;
16
+ declare function getCameraPermissionsAsync(): Promise<PermissionResponse>;
17
+ declare function requestCameraPermissionsAsync(): Promise<PermissionResponse>;
18
+ declare function getMicrophonePermissionsAsync(): Promise<PermissionResponse>;
19
+ declare function requestMicrophonePermissionsAsync(): Promise<PermissionResponse>;
20
+ export declare const Camera: {
21
+ getCameraPermissionsAsync: typeof getCameraPermissionsAsync;
22
+ requestCameraPermissionsAsync: typeof requestCameraPermissionsAsync;
23
+ getMicrophonePermissionsAsync: typeof getMicrophonePermissionsAsync;
24
+ requestMicrophonePermissionsAsync: typeof requestMicrophonePermissionsAsync;
25
+ scanFromURLAsync: typeof scanFromURLAsync;
26
+ };
27
+ export declare const cameraPermissionMethods: {
28
+ getMethod: typeof getCameraPermissionsAsync;
29
+ requestMethod: typeof requestCameraPermissionsAsync;
30
+ };
31
+ export declare const microphonePermissionMethods: {
32
+ getMethod: typeof getMicrophonePermissionsAsync;
33
+ requestMethod: typeof requestMicrophonePermissionsAsync;
34
+ };
35
+ export {};
@@ -0,0 +1,68 @@
1
+ import { UnavailabilityError } from 'expo-modules-core';
2
+ import { CAMERA_PACKAGE_NAME, MODERN_SCANNER_EVENT } from './constants.js';
3
+ import { expoCamera } from './native-module.js';
4
+ /** Whether the device has a camera, the permissions are a separate question */
5
+ export async function isCameraAvailableAsync() {
6
+ if (!expoCamera.isAvailableAsync) {
7
+ throw new UnavailabilityError(CAMERA_PACKAGE_NAME, 'isAvailableAsync');
8
+ }
9
+ return expoCamera.isAvailableAsync();
10
+ }
11
+ /** iOS only */
12
+ export async function getAvailableVideoCodecsAsync() {
13
+ if (!expoCamera.getAvailableVideoCodecsAsync) {
14
+ throw new UnavailabilityError('Camera', 'getAvailableVideoCodecsAsync');
15
+ }
16
+ return expoCamera.getAvailableVideoCodecsAsync();
17
+ }
18
+ /** Whether the device has `DataScannerViewController` (iOS 16+) or Google's code scanner */
19
+ export function isModernBarcodeScannerAvailable() {
20
+ return expoCamera.isModernBarcodeScannerAvailable;
21
+ }
22
+ /** A device without the modern scanner does nothing here */
23
+ export async function launchScanner(options = { barcodeTypes: [] }) {
24
+ if (expoCamera.isModernBarcodeScannerAvailable) {
25
+ await expoCamera.launchScanner(options);
26
+ }
27
+ }
28
+ /** Dismisses the scanner on iOS, on Android it closes itself after a scan */
29
+ export async function dismissScanner() {
30
+ if (expoCamera.isModernBarcodeScannerAvailable) {
31
+ await expoCamera.dismissScanner();
32
+ }
33
+ }
34
+ export function onModernBarcodeScanned(listener) {
35
+ return expoCamera.addListener(MODERN_SCANNER_EVENT, listener);
36
+ }
37
+ /** Only QR codes are scanned on iOS */
38
+ export async function scanFromURLAsync(url, barcodeTypes = ['qr']) {
39
+ return expoCamera.scanFromURLAsync(url, barcodeTypes);
40
+ }
41
+ async function getCameraPermissionsAsync() {
42
+ return expoCamera.getCameraPermissionsAsync();
43
+ }
44
+ async function requestCameraPermissionsAsync() {
45
+ return expoCamera.requestCameraPermissionsAsync();
46
+ }
47
+ async function getMicrophonePermissionsAsync() {
48
+ return expoCamera.getMicrophonePermissionsAsync();
49
+ }
50
+ async function requestMicrophonePermissionsAsync() {
51
+ return expoCamera.requestMicrophonePermissionsAsync();
52
+ }
53
+ export const Camera = {
54
+ getCameraPermissionsAsync,
55
+ requestCameraPermissionsAsync,
56
+ getMicrophonePermissionsAsync,
57
+ requestMicrophonePermissionsAsync,
58
+ scanFromURLAsync,
59
+ };
60
+ // What every adapter's `createPermissionHook` binds, `useCameraPermissions` and its twin
61
+ export const cameraPermissionMethods = {
62
+ getMethod: getCameraPermissionsAsync,
63
+ requestMethod: requestCameraPermissionsAsync,
64
+ };
65
+ export const microphonePermissionMethods = {
66
+ getMethod: getMicrophonePermissionsAsync,
67
+ requestMethod: requestMicrophonePermissionsAsync,
68
+ };
@@ -0,0 +1,3 @@
1
+ export type ICameraViewEvents = (props: object) => Record<string, unknown>;
2
+ /** One per view, the throttle of the barcode events belongs to the view */
3
+ export declare function createCameraViewEvents(): ICameraViewEvents;
@@ -0,0 +1,60 @@
1
+ import { BARCODE_EVENT_THROTTLE_MS } from './constants.js';
2
+ import { handlePictureSaved } from './picture-options.js';
3
+ function hasField(value, key, kind) {
4
+ return typeof Reflect.get(Object(value), key) === kind;
5
+ }
6
+ const isMountError = (value) => hasField(value, 'message', 'string');
7
+ const isOrientation = (value) => hasField(value, 'orientation', 'string');
8
+ const isLenses = (value) => Array.isArray(Reflect.get(Object(value), 'lenses'));
9
+ const isScanningResult = (value) => hasField(value, 'type', 'string') && hasField(value, 'data', 'string');
10
+ function isPictureSaved(value) {
11
+ return hasField(value, 'id', 'number') && hasField(value, 'data', 'object');
12
+ }
13
+ // Hands the callback the payload inside the native event, and only when it has the expected shape
14
+ function forward(props, name, isPayload) {
15
+ return (event) => {
16
+ const callback = Reflect.get(props, name);
17
+ if (typeof callback === 'function' && isPayload(event.nativeEvent)) {
18
+ callback(event.nativeEvent);
19
+ }
20
+ };
21
+ }
22
+ /** One per view, the throttle of the barcode events belongs to the view */
23
+ export function createCameraViewEvents() {
24
+ const lastScans = new Map();
25
+ // Equal events of one type inside the window are the same scan seen again
26
+ const throttledScan = (callback) => (event) => {
27
+ const result = event.nativeEvent;
28
+ if (!isScanningResult(result))
29
+ return;
30
+ const json = JSON.stringify(result);
31
+ const now = Date.now();
32
+ const last = lastScans.get(result.type);
33
+ if (last?.json === json && now - last.at < BARCODE_EVENT_THROTTLE_MS) {
34
+ return;
35
+ }
36
+ callback(result);
37
+ lastScans.set(result.type, { json, at: now });
38
+ };
39
+ return props => {
40
+ const onBarcodeScanned = Reflect.get(props, 'onBarcodeScanned');
41
+ const onCameraReady = Reflect.get(props, 'onCameraReady');
42
+ return {
43
+ onCameraReady: () => {
44
+ if (typeof onCameraReady === 'function')
45
+ onCameraReady();
46
+ },
47
+ onMountError: forward(props, 'onMountError', isMountError),
48
+ onBarcodeScanned: typeof onBarcodeScanned === 'function'
49
+ ? throttledScan(result => onBarcodeScanned(result))
50
+ : undefined,
51
+ onAvailableLensesChanged: forward(props, 'onAvailableLensesChanged', isLenses),
52
+ onResponsiveOrientationChanged: forward(props, 'onResponsiveOrientationChanged', isOrientation),
53
+ onPictureSaved: (event) => {
54
+ if (isPictureSaved(event.nativeEvent)) {
55
+ handlePictureSaved({ nativeEvent: event.nativeEvent });
56
+ }
57
+ },
58
+ };
59
+ };
60
+ }
@@ -0,0 +1,26 @@
1
+ import type { ISymbioteNode } from '@symbiote-native/engine';
2
+ import type { ICameraPictureRef } from './native-module';
3
+ import type { ICameraCapturedPicture, ICameraPictureOptions, ICameraRecordingOptions, ICameraSupportedFeatures } from './types';
4
+ export type ICameraViewHandle = {
5
+ /** Waits for `onCameraReady` first. With `pictureRef` it resolves to a native image reference */
6
+ takePictureAsync(options: ICameraPictureOptions & {
7
+ pictureRef: true;
8
+ }): Promise<ICameraPictureRef | undefined>;
9
+ takePictureAsync(options?: ICameraPictureOptions): Promise<ICameraCapturedPicture | undefined>;
10
+ /** Resolves when `stopRecording` runs, a limit is reached or the preview stops */
11
+ recordAsync(options?: ICameraRecordingOptions): Promise<{
12
+ uri: string;
13
+ } | undefined>;
14
+ /** Pauses or resumes the recording, iOS 18 and later on iOS */
15
+ toggleRecordingAsync(): Promise<void>;
16
+ stopRecording(): void;
17
+ pausePreview(): Promise<void>;
18
+ resumePreview(): Promise<void>;
19
+ getAvailablePictureSizesAsync(): Promise<string[]>;
20
+ /** Lenses exist on iOS only */
21
+ getAvailableLensesAsync(): Promise<string[]>;
22
+ getSupportedFeatures(): ICameraSupportedFeatures;
23
+ /** The host node of the mounted view, `createCameraTextureAsync` of a GL view takes it */
24
+ getHostNode(): ISymbioteNode | null;
25
+ };
26
+ export declare function createCameraViewHandle(getNode: () => ISymbioteNode | null | undefined): ICameraViewHandle;
@@ -0,0 +1,40 @@
1
+ import { Platform, requireNativeModule } from 'expo-modules-core';
2
+ import { defineExpoViewMethods, getNativeTag } from '@symbiote-native/engine';
3
+ import { CAMERA_MODULE_NAME } from './constants.js';
4
+ import { expoCamera } from './native-module.js';
5
+ import { ensurePictureOptions, ensureRecordingOptions } from './picture-options.js';
6
+ const callViewFunction = defineExpoViewMethods(requireNativeModule, CAMERA_MODULE_NAME);
7
+ export function createCameraViewHandle(getNode) {
8
+ // Like upstream's `ref.current?.`, a view that has not committed yet answers nothing
9
+ function call(method, args = []) {
10
+ const node = getNode();
11
+ if (!node || getNativeTag(node) === undefined)
12
+ return undefined;
13
+ return callViewFunction(node, method, args);
14
+ }
15
+ async function takePictureAsync(options) {
16
+ const pictureOptions = ensurePictureOptions(options);
17
+ // The picture ref is an iOS feature, Android answers a plain picture
18
+ if (Platform.select({ ios: Boolean(options?.pictureRef), default: false })) {
19
+ return call('takePictureRef', [pictureOptions]);
20
+ }
21
+ return call('takePicture', [pictureOptions]);
22
+ }
23
+ return {
24
+ takePictureAsync,
25
+ recordAsync: async (options) => call('record', [ensureRecordingOptions(options)]),
26
+ toggleRecordingAsync: async () => call('toggleRecording'),
27
+ stopRecording: () => {
28
+ call('stopRecording');
29
+ },
30
+ pausePreview: async () => call('pausePreview'),
31
+ resumePreview: async () => call('resumePreview'),
32
+ getAvailablePictureSizesAsync: async () => (await call('getAvailablePictureSizes')) ?? [],
33
+ getAvailableLensesAsync: async () => (await call('getAvailableLenses')) ?? [],
34
+ getSupportedFeatures: () => ({
35
+ isModernBarcodeScannerAvailable: expoCamera.isModernBarcodeScannerAvailable,
36
+ toggleRecordingAsyncAvailable: expoCamera.toggleRecordingAsyncAvailable,
37
+ }),
38
+ getHostNode: () => getNode() ?? null,
39
+ };
40
+ }
@@ -0,0 +1,12 @@
1
+ import type { IDescriptor } from '@symbiote-native/components';
2
+ import type { ISymbioteNode } from '@symbiote-native/engine';
3
+ import type { ICameraViewHandle } from './camera-view-handle';
4
+ export declare const cameraViewName: () => string;
5
+ export declare const ensureCameraViewRegistered: () => boolean;
6
+ export type ICameraView = {
7
+ handle: ICameraViewHandle;
8
+ /** `null` means the view cannot register and the caller renders nothing */
9
+ render(props: object): IDescriptor | null;
10
+ };
11
+ /** One per mounted view, `getNode` answers the host node once it exists */
12
+ export declare function createCameraView(getNode: () => ISymbioteNode | null | undefined): ICameraView;