@octane-xplat/camera 0.7.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.
@@ -0,0 +1,165 @@
1
+ import { cameraHardwarePresent, ensureCameraPermission } from "./camera-permission.mobile.js";
2
+ import { applyEscapeProps, layoutChildProps, nativeAccessibilityRole, nativeAccessibilityState } from "./layout-child.js";
3
+ import { cx } from "./cx.mobile.js";
4
+ import { defineUniversalComponent, hookSlots, registerElement, universalPlan, universalProps, universalValue, useCallback, useEffect, useRef } from "@nativescript-community/octane";
5
+ import { Application, View } from "@nativescript/core";
6
+ //#region src/CameraView.android.tsrx
7
+ var _hs$ = /* @__PURE__ */ hookSlots(4);
8
+ var _h$0 = /* @__PURE__ */ Symbol(_hs$);
9
+ var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
10
+ var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
11
+ var _h$3 = /* @__PURE__ */ Symbol(_hs$ + 3);
12
+ var __octaneUniversalPlan0 = universalPlan("nativescript", {
13
+ "kind": "host",
14
+ "type": "gridlayout",
15
+ "propsSlot": 0,
16
+ "children": [{
17
+ "kind": "host",
18
+ "type": "camerapreview",
19
+ "props": {
20
+ "row": 0,
21
+ "col": 0
22
+ },
23
+ "bindings": [["ref", 1]]
24
+ }]
25
+ });
26
+ var CameraPreview = class extends View {
27
+ createNativeView() {
28
+ const nativeClass = globalThis.com.octanexplat.camera.CameraPreviewView;
29
+ return new nativeClass(this._context);
30
+ }
31
+ };
32
+ registerElement("camerapreview", CameraPreview);
33
+ var CameraView = defineUniversalComponent("nativescript", function CameraView(props) {
34
+ const host = useRef(null, _h$0);
35
+ const active = props.active !== false;
36
+ const facing = props.facing ?? "back";
37
+ const bindHost = useCallback((view) => {
38
+ host.current = view;
39
+ }, [], _h$1);
40
+ useEffect(() => {
41
+ const nativeView = host.current?.android;
42
+ if (!nativeView) return;
43
+ let disposed = false;
44
+ let provider;
45
+ let preview;
46
+ nativeView.setCameraActive(active);
47
+ if (!active) return () => nativeView.setCameraActive(false);
48
+ const fail = (message) => props.onError?.({ message });
49
+ if (!cameraHardwarePresent()) {
50
+ fail("camera unsupported");
51
+ return () => nativeView.setCameraActive(false);
52
+ }
53
+ ensureCameraPermission().then((granted) => {
54
+ if (disposed) return;
55
+ if (!granted) {
56
+ fail("camera permission denied");
57
+ return;
58
+ }
59
+ try {
60
+ const context = Application.android.context;
61
+ const cameraX = globalThis.androidx.camera;
62
+ const future = cameraX.lifecycle.ProcessCameraProvider.getInstance(context);
63
+ const executor = globalThis.androidx.core.content.ContextCompat.getMainExecutor(context);
64
+ future.addListener(new java.lang.Runnable({ run() {
65
+ if (disposed) return;
66
+ try {
67
+ provider = future.get();
68
+ const selectorClass = cameraX.core.CameraSelector;
69
+ const lens = facing === "front" ? selectorClass.LENS_FACING_FRONT : selectorClass.LENS_FACING_BACK;
70
+ const selector = new selectorClass.Builder().requireLensFacing(lens).build();
71
+ preview = new cameraX.core.Preview.Builder().build();
72
+ preview.setSurfaceProvider(nativeView.getSurfaceProvider());
73
+ provider.bindToLifecycle(nativeView, selector, preview);
74
+ props.onReady?.();
75
+ } catch (error) {
76
+ fail(error?.message ?? "camera error");
77
+ }
78
+ } }), executor);
79
+ } catch (error) {
80
+ fail(error?.message ?? "camera error");
81
+ }
82
+ });
83
+ return () => {
84
+ disposed = true;
85
+ nativeView.setCameraActive(false);
86
+ if (provider && preview) try {
87
+ provider.unbind(preview);
88
+ } catch {}
89
+ };
90
+ }, [active, facing], _h$2);
91
+ useEffect(() => {
92
+ const view = host.current;
93
+ if (!view) return;
94
+ applyEscapeProps(view.android, props);
95
+ props.bind?.({ native: view.android });
96
+ }, [props.bind, props.android], _h$3);
97
+ return universalValue(__octaneUniversalPlan0, [universalProps([
98
+ [
99
+ "set",
100
+ "id",
101
+ props.id
102
+ ],
103
+ [
104
+ "set",
105
+ "rows",
106
+ "*"
107
+ ],
108
+ [
109
+ "set",
110
+ "columns",
111
+ "*"
112
+ ],
113
+ [
114
+ "set",
115
+ "className",
116
+ cx("vx-stack", "vx-cameraview", props.className)
117
+ ],
118
+ [
119
+ "set",
120
+ "style",
121
+ {
122
+ backgroundColor: "#000",
123
+ ...props.style
124
+ }
125
+ ],
126
+ ["spread", layoutChildProps(props)],
127
+ [
128
+ "set",
129
+ "accessible",
130
+ props.accessible
131
+ ],
132
+ [
133
+ "set",
134
+ "accessibilityLabel",
135
+ props.accessibilityLabel
136
+ ],
137
+ [
138
+ "set",
139
+ "accessibilityRole",
140
+ nativeAccessibilityRole(props.accessibilityRole)
141
+ ],
142
+ [
143
+ "set",
144
+ "accessibilityHint",
145
+ props.accessibilityHint
146
+ ],
147
+ [
148
+ "set",
149
+ "accessibilityValue",
150
+ props.accessibilityValue
151
+ ],
152
+ [
153
+ "set",
154
+ "accessibilityState",
155
+ nativeAccessibilityState(props.accessibilityState)
156
+ ],
157
+ [
158
+ "set",
159
+ "accessibilityLiveRegion",
160
+ props.accessibilityLiveRegion
161
+ ]
162
+ ]), bindHost]);
163
+ }, { "module": "@nativescript-community/octane" });
164
+ //#endregion
165
+ export { CameraView };
@@ -0,0 +1,130 @@
1
+ import { cameraHardwarePresent, ensureCameraPermission } from "./camera-permission.mobile.js";
2
+ import { startCameraPreview } from "./camera-session.ios.js";
3
+ import { applyEscapeProps, layoutChildProps, nativeAccessibilityRole, nativeAccessibilityState } from "./layout-child.js";
4
+ import { cx } from "./cx.mobile.js";
5
+ import { defineUniversalComponent, hookSlots, universalPlan, universalProps, universalValue, useCallback, useEffect, useRef } from "@nativescript-community/octane";
6
+ //#region src/CameraView.ios.tsrx
7
+ var _hs$ = /* @__PURE__ */ hookSlots(4);
8
+ var _h$0 = /* @__PURE__ */ Symbol(_hs$);
9
+ var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
10
+ var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
11
+ var _h$3 = /* @__PURE__ */ Symbol(_hs$ + 3);
12
+ var __octaneUniversalPlan0 = universalPlan("nativescript", {
13
+ "kind": "host",
14
+ "type": "gridlayout",
15
+ "propsSlot": 0
16
+ });
17
+ var CameraView = defineUniversalComponent("nativescript", function CameraView(props) {
18
+ const host = useRef(null, _h$0);
19
+ const active = props.active !== false;
20
+ const facing = props.facing ?? "back";
21
+ const bindHost = useCallback((view) => {
22
+ host.current = view;
23
+ }, [], _h$1);
24
+ useEffect(() => {
25
+ const view = host.current;
26
+ if (!view || !active) return;
27
+ let disposed = false;
28
+ let stopPreview;
29
+ const fail = (message) => props.onError?.({ message });
30
+ if (!cameraHardwarePresent()) {
31
+ fail("camera unsupported");
32
+ return;
33
+ }
34
+ ensureCameraPermission().then((granted) => {
35
+ if (disposed) return;
36
+ if (!granted) {
37
+ fail("camera permission denied");
38
+ return;
39
+ }
40
+ try {
41
+ stopPreview = startCameraPreview(view, facing, props.onReady);
42
+ } catch (error) {
43
+ fail(error?.message ?? "camera error");
44
+ }
45
+ });
46
+ return () => {
47
+ disposed = true;
48
+ stopPreview?.();
49
+ };
50
+ }, [active, facing], _h$2);
51
+ useEffect(() => {
52
+ const view = host.current;
53
+ if (!view) return;
54
+ applyEscapeProps(view.ios ?? view.nativeViewProtected, props);
55
+ props.bind?.({ native: view.ios ?? view.nativeViewProtected });
56
+ }, [props.bind, props.ios], _h$3);
57
+ return universalValue(__octaneUniversalPlan0, [universalProps([
58
+ [
59
+ "set",
60
+ "ref",
61
+ bindHost
62
+ ],
63
+ [
64
+ "set",
65
+ "id",
66
+ props.id
67
+ ],
68
+ [
69
+ "set",
70
+ "rows",
71
+ "*"
72
+ ],
73
+ [
74
+ "set",
75
+ "columns",
76
+ "*"
77
+ ],
78
+ [
79
+ "set",
80
+ "className",
81
+ cx("vx-stack", "vx-cameraview", props.className)
82
+ ],
83
+ [
84
+ "set",
85
+ "style",
86
+ {
87
+ backgroundColor: "#000",
88
+ ...props.style
89
+ }
90
+ ],
91
+ ["spread", layoutChildProps(props)],
92
+ [
93
+ "set",
94
+ "accessible",
95
+ props.accessible
96
+ ],
97
+ [
98
+ "set",
99
+ "accessibilityLabel",
100
+ props.accessibilityLabel
101
+ ],
102
+ [
103
+ "set",
104
+ "accessibilityRole",
105
+ nativeAccessibilityRole(props.accessibilityRole)
106
+ ],
107
+ [
108
+ "set",
109
+ "accessibilityHint",
110
+ props.accessibilityHint
111
+ ],
112
+ [
113
+ "set",
114
+ "accessibilityValue",
115
+ props.accessibilityValue
116
+ ],
117
+ [
118
+ "set",
119
+ "accessibilityState",
120
+ nativeAccessibilityState(props.accessibilityState)
121
+ ],
122
+ [
123
+ "set",
124
+ "accessibilityLiveRegion",
125
+ props.accessibilityLiveRegion
126
+ ]
127
+ ])]);
128
+ }, { "module": "@nativescript-community/octane" });
129
+ //#endregion
130
+ export { CameraView };
@@ -0,0 +1,45 @@
1
+ import { Application, isAndroid, isIOS } from "@nativescript/core";
2
+ //#region src/camera-permission.mobile.ts
3
+ var requestCode = 7719;
4
+ function cameraHardwarePresent() {
5
+ if (isIOS) return typeof globalThis.AVCaptureDevice !== "undefined";
6
+ if (!isAndroid) return false;
7
+ try {
8
+ return (Application.android?.context ?? Application.android?.startActivity)?.packageManager?.hasSystemFeature("android.hardware.camera.any") !== false;
9
+ } catch {
10
+ return true;
11
+ }
12
+ }
13
+ async function ensureCameraPermission() {
14
+ try {
15
+ if (isIOS) {
16
+ const av = globalThis.AVCaptureDevice;
17
+ const mediaType = globalThis.AVMediaTypeVideo ?? "vide";
18
+ const status = av?.authorizationStatusForMediaType?.(mediaType);
19
+ if (status === 3) return true;
20
+ if (status === 1 || status === 2) return false;
21
+ return await new Promise((resolve) => av.requestAccessForMediaTypeCompletionHandler(mediaType, resolve));
22
+ }
23
+ if (isAndroid) {
24
+ const permission = "android.permission.CAMERA";
25
+ const activity = Application.android.foregroundActivity ?? Application.android.startActivity;
26
+ if (activity?.checkSelfPermission?.(permission) === android.content.pm.PackageManager.PERMISSION_GRANTED) return true;
27
+ if (!activity) return false;
28
+ return await new Promise((resolve) => {
29
+ const currentRequest = requestCode++;
30
+ const onResult = (args) => {
31
+ if (args.requestCode !== currentRequest) return;
32
+ Application.android.off(Application.android.activityRequestPermissionsEvent, onResult);
33
+ resolve(args.grantResults?.[0] === android.content.pm.PackageManager.PERMISSION_GRANTED);
34
+ };
35
+ Application.android.on(Application.android.activityRequestPermissionsEvent, onResult);
36
+ activity.requestPermissions([permission], currentRequest);
37
+ });
38
+ }
39
+ } catch {
40
+ return false;
41
+ }
42
+ return false;
43
+ }
44
+ //#endregion
45
+ export { cameraHardwarePresent, ensureCameraPermission };
@@ -0,0 +1,45 @@
1
+ //#region src/camera-session.ios.ts
2
+ /** Attach an AVFoundation preview layer to a NativeScript UIView host. */
3
+ function startCameraPreview(view, facing, onReady) {
4
+ const av = globalThis;
5
+ const mediaType = av.AVMediaTypeVideo ?? "vide";
6
+ const desiredPosition = facing === "front" ? av.AVCaptureDevicePositionFront ?? 2 : av.AVCaptureDevicePositionBack ?? 1;
7
+ const devices = av.AVCaptureDevice.devicesWithMediaType(mediaType);
8
+ let device = null;
9
+ for (let index = 0; index < devices.count; index++) {
10
+ const candidate = devices.objectAtIndex(index);
11
+ if (candidate.position === desiredPosition) {
12
+ device = candidate;
13
+ break;
14
+ }
15
+ }
16
+ if (!device) throw new Error("camera unavailable");
17
+ const session = av.AVCaptureSession.new();
18
+ const input = av.AVCaptureDeviceInput.deviceInputWithDeviceError(device);
19
+ if (!input || !session.canAddInput(input)) throw new Error("camera input unavailable");
20
+ session.addInput(input);
21
+ const layer = av.AVCaptureVideoPreviewLayer.layerWithSession(session);
22
+ layer.videoGravity = "resizeAspectFill";
23
+ const connection = layer.connection;
24
+ if (connection) {
25
+ connection.automaticallyAdjustsVideoMirroring = false;
26
+ if (connection.isVideoMirroringSupported) connection.isVideoMirrored = facing === "front";
27
+ }
28
+ const nativeView = view.ios ?? view.nativeViewProtected;
29
+ if (!nativeView?.layer) throw new Error("camera preview host unavailable");
30
+ nativeView.layer.addSublayer(layer);
31
+ const updateFrame = () => {
32
+ layer.frame = nativeView.bounds;
33
+ };
34
+ view.on?.("layoutChanged", updateFrame);
35
+ updateFrame();
36
+ session.startRunning();
37
+ onReady?.();
38
+ return () => {
39
+ view.off?.("layoutChanged", updateFrame);
40
+ session.stopRunning();
41
+ layer.removeFromSuperlayer();
42
+ };
43
+ }
44
+ //#endregion
45
+ export { startCameraPreview };
@@ -0,0 +1,8 @@
1
+ //#region src/cx.mobile.ts
2
+ /** NativeScript's universal driver stringifies className values; normalize
3
+ * arrays here so they stay space separated. */
4
+ function cx(...parts) {
5
+ return parts.flat(Infinity).filter(Boolean).join(" ");
6
+ }
7
+ //#endregion
8
+ export { cx };
@@ -0,0 +1,2 @@
1
+ import { CameraView } from "./src/CameraView";
2
+ export { CameraView };
@@ -0,0 +1,53 @@
1
+ import { isIOS } from "@nativescript/core";
2
+ //#region src/layout-child.ts
3
+ /** NativeScript reads layout fields from a child view during parent layout. */
4
+ function layoutChildProps(props) {
5
+ const result = {};
6
+ for (const key of [
7
+ "row",
8
+ "col",
9
+ "rowSpan",
10
+ "colSpan",
11
+ "dock",
12
+ "left",
13
+ "top",
14
+ "flexGrow",
15
+ "flexShrink",
16
+ "alignSelf",
17
+ "order"
18
+ ]) if (props[key] !== void 0) result[key] = props[key];
19
+ return result;
20
+ }
21
+ function applyEscapeProps(view, props) {
22
+ if (!view) return;
23
+ const bag = isIOS ? props.ios : props.android;
24
+ if (bag) Object.assign(view, bag);
25
+ }
26
+ var nativeRoles = {
27
+ button: "button",
28
+ link: "link",
29
+ search: "search",
30
+ image: "image",
31
+ heading: "header",
32
+ adjustable: "adjustable",
33
+ summary: "summary",
34
+ text: "text",
35
+ none: "none",
36
+ progressbar: "progressBar",
37
+ checkbox: "checkbox",
38
+ switch: "switch",
39
+ radio: "radioButton",
40
+ spinbutton: "spinButton",
41
+ tab: "button"
42
+ };
43
+ function nativeAccessibilityRole(role) {
44
+ return role ? nativeRoles[role] : void 0;
45
+ }
46
+ function nativeAccessibilityState(state) {
47
+ if (state?.disabled) return "disabled";
48
+ if (state?.selected) return "selected";
49
+ if (state?.checked === true) return "checked";
50
+ if (state?.checked === false) return "unchecked";
51
+ }
52
+ //#endregion
53
+ export { applyEscapeProps, layoutChildProps, nativeAccessibilityRole, nativeAccessibilityState };
@@ -0,0 +1,8 @@
1
+ {
2
+ "version": 1,
3
+ "buildId": "6078389e-4315-40ab-b7f8-31cd00bc0232",
4
+ "mode": "production",
5
+ "capabilities": {
6
+ "independentHydration": false
7
+ }
8
+ }