@symbiote-native/camera 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +254 -2
- package/build/angular/camera-view/index.d.ts +43 -0
- package/build/angular/camera-view/index.js +101 -0
- package/build/angular/index.d.ts +3 -0
- package/build/angular/index.js +4 -0
- package/build/angular/services/camera-permissions.service.d.ts +8 -0
- package/build/angular/services/camera-permissions.service.js +82 -0
- package/build/core/android-barcode.d.ts +50 -0
- package/build/core/android-barcode.js +2 -0
- package/build/core/camera-api.d.ts +35 -0
- package/build/core/camera-api.js +68 -0
- package/build/core/camera-view-events.d.ts +3 -0
- package/build/core/camera-view-events.js +60 -0
- package/build/core/camera-view-handle.d.ts +26 -0
- package/build/core/camera-view-handle.js +46 -0
- package/build/core/camera-view.d.ts +12 -0
- package/build/core/camera-view.js +38 -0
- package/build/core/constants.d.ts +6 -0
- package/build/core/constants.js +7 -0
- package/build/core/index.d.ts +9 -0
- package/build/core/index.js +5 -0
- package/build/core/native-module.d.ts +28 -0
- package/build/core/native-module.js +3 -0
- package/build/core/picture-options.d.ts +9 -0
- package/build/core/picture-options.js +29 -0
- package/build/core/picture-ref.d.ts +2 -0
- package/build/core/picture-ref.js +3 -0
- package/build/core/props.d.ts +4 -0
- package/build/core/props.js +26 -0
- package/build/core/types.d.ts +170 -0
- package/build/core/types.js +1 -0
- package/build/react/camera-view.d.ts +8 -0
- package/build/react/camera-view.js +6 -0
- package/build/react/index.d.ts +5 -0
- package/build/react/index.js +4 -0
- package/build/react/use-camera-permissions.d.ts +9 -0
- package/build/react/use-camera-permissions.js +5 -0
- package/build/solid/camera-view.d.ts +33 -0
- package/build/solid/camera-view.js +4 -0
- package/build/solid/index.d.ts +4 -0
- package/build/solid/index.js +4 -0
- package/build/solid/use-camera-permissions.d.ts +10 -0
- package/build/solid/use-camera-permissions.js +5 -0
- package/build/svelte/camera-view.svelte +32 -0
- package/build/svelte/camera-view.svelte.d.ts +22 -0
- package/build/svelte/index.d.ts +4 -0
- package/build/svelte/index.js +4 -0
- package/build/svelte/use-camera-permissions.svelte.d.ts +9 -0
- package/build/svelte/use-camera-permissions.svelte.js +5 -0
- package/build/vue/camera-view.d.ts +2 -0
- package/build/vue/camera-view.js +4 -0
- package/build/vue/index.d.ts +4 -0
- package/build/vue/index.js +4 -0
- package/build/vue/use-camera-permissions.d.ts +10 -0
- package/build/vue/use-camera-permissions.js +5 -0
- package/build-ngc/angular/camera-view/index.d.ts +46 -0
- package/build-ngc/angular/camera-view/index.js +60 -0
- package/build-ngc/angular/camera-view/index.js.map +1 -0
- package/build-ngc/angular/index.d.ts +3 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/camera-permissions.service.d.ts +13 -0
- package/build-ngc/angular/services/camera-permissions.service.js +28 -0
- package/build-ngc/angular/services/camera-permissions.service.js.map +1 -0
- package/build-ngc/core/android-barcode.d.ts +50 -0
- package/build-ngc/core/android-barcode.js +3 -0
- package/build-ngc/core/android-barcode.js.map +1 -0
- package/build-ngc/core/camera-api.d.ts +35 -0
- package/build-ngc/core/camera-api.js +69 -0
- package/build-ngc/core/camera-api.js.map +1 -0
- package/build-ngc/core/camera-view-events.d.ts +3 -0
- package/build-ngc/core/camera-view-events.js +61 -0
- package/build-ngc/core/camera-view-events.js.map +1 -0
- package/build-ngc/core/camera-view-handle.d.ts +26 -0
- package/build-ngc/core/camera-view-handle.js +47 -0
- package/build-ngc/core/camera-view-handle.js.map +1 -0
- package/build-ngc/core/camera-view.d.ts +12 -0
- package/build-ngc/core/camera-view.js +39 -0
- package/build-ngc/core/camera-view.js.map +1 -0
- package/build-ngc/core/constants.d.ts +6 -0
- package/build-ngc/core/constants.js +8 -0
- package/build-ngc/core/constants.js.map +1 -0
- package/build-ngc/core/index.d.ts +9 -0
- package/build-ngc/core/index.js +6 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +28 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/picture-options.d.ts +9 -0
- package/build-ngc/core/picture-options.js +30 -0
- package/build-ngc/core/picture-options.js.map +1 -0
- package/build-ngc/core/picture-ref.d.ts +2 -0
- package/build-ngc/core/picture-ref.js +4 -0
- package/build-ngc/core/picture-ref.js.map +1 -0
- package/build-ngc/core/props.d.ts +4 -0
- package/build-ngc/core/props.js +27 -0
- package/build-ngc/core/props.js.map +1 -0
- package/build-ngc/core/types.d.ts +170 -0
- package/build-ngc/core/types.js +2 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +22 -0
- package/package.json +144 -3
- package/src/angular/camera-view/index.ts +95 -0
- package/src/angular/index.ts +8 -0
- package/src/angular/services/camera-permissions.service.ts +28 -0
- package/src/core/android-barcode.ts +51 -0
- package/src/core/camera-api.ts +99 -0
- package/src/core/camera-view-events.ts +108 -0
- package/src/core/camera-view-handle.ts +105 -0
- package/src/core/camera-view.ts +56 -0
- package/src/core/constants.ts +11 -0
- package/src/core/index.ts +57 -0
- package/src/core/native-module.ts +58 -0
- package/src/core/picture-options.ts +46 -0
- package/src/core/picture-ref.ts +4 -0
- package/src/core/props.ts +31 -0
- package/src/core/types.ts +212 -0
- package/src/react/camera-view.ts +17 -0
- package/src/react/index.ts +10 -0
- package/src/react/use-camera-permissions.ts +21 -0
- package/src/solid/camera-view.ts +9 -0
- package/src/solid/index.ts +9 -0
- package/src/solid/use-camera-permissions.ts +22 -0
- package/src/svelte/camera-view.svelte +32 -0
- package/src/svelte/index.ts +9 -0
- package/src/svelte/svelte-ambient.d.ts +3 -0
- package/src/svelte/svelte-compile.test-helper.ts +6 -0
- package/src/svelte/use-camera-permissions.svelte.ts +19 -0
- package/src/vue/camera-view.ts +8 -0
- package/src/vue/index.ts +9 -0
- 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,255 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @symbiote-native/camera
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
Built the same way as [`@symbiote-native/clipboard`](../clipboard)'s `ClipboardPasteButton`, an
|
|
8
|
+
`expo-modules-core` native view reached through `requireNativeViewManager` (see the
|
|
9
|
+
`symbiote-expo-native-module` project skill: why `expo-modules-core` is depended on directly and never
|
|
10
|
+
the `expo` meta-package, why the upstream JS is hand-ported into `core/`, and how autolinking finds the
|
|
11
|
+
native module). One render function in `core/` returns a descriptor and every adapter only turns it into
|
|
12
|
+
its own element.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
**New app:**
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @symbiote-native/cli new my-app --camera
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Existing SymbioteNative app:**
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @symbiote-native/cli add --camera
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Either way: installs `@symbiote-native/camera` and wires the native autolinking automatically,
|
|
29
|
+
see [`@symbiote-native/cli`](../cli).
|
|
30
|
+
|
|
31
|
+
<details>
|
|
32
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @symbiote-native/camera
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Depends on `expo-camera` and `expo-modules-core` directly (regular dependencies, pinned to exact
|
|
39
|
+
versions, since this package's `core/` is hand-ported against one specific native API shape).
|
|
40
|
+
Never install `expo-camera` yourself, and never add the `expo` package to this project: it bundles
|
|
41
|
+
its own Metro/Babel pipeline that conflicts with this project's own.
|
|
42
|
+
|
|
43
|
+
### Required one-time step: native autolinking wiring
|
|
44
|
+
|
|
45
|
+
`expo-camera`'s native code is discovered by `expo-modules-autolinking`, not by RN's own
|
|
46
|
+
`react-native.config.cjs` mechanism. Wire it into the native host app **once**, it covers this package
|
|
47
|
+
and every other `expo-modules-core` package with zero further changes. The steps and the
|
|
48
|
+
mechanics behind them are in the `symbiote-expo-native-module` skill and in
|
|
49
|
+
[`@symbiote-native/network`](../network)'s README; the reference app is `examples/expo-react`.
|
|
50
|
+
|
|
51
|
+
### Permissions
|
|
52
|
+
|
|
53
|
+
The package declares what `expo-camera`'s config plugin adds, and the `expo-modules-link` postinstall
|
|
54
|
+
writes it into the app:
|
|
55
|
+
|
|
56
|
+
| File | Entry |
|
|
57
|
+
| --------------------- | ---------------------------------------------------------- |
|
|
58
|
+
| `Info.plist` | `NSCameraUsageDescription`, `NSMicrophoneUsageDescription` |
|
|
59
|
+
| `AndroidManifest.xml` | `CAMERA`, `RECORD_AUDIO` |
|
|
60
|
+
|
|
61
|
+
iOS wants the microphone text even if you only take pictures, because the same view can record. Reword
|
|
62
|
+
both texts to fit your app.
|
|
63
|
+
|
|
64
|
+
</details>
|
|
65
|
+
|
|
66
|
+
## Shape
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
src/core/ camera-view.ts - createCameraView(getNode): render, the handle (camera-view-handle.ts)
|
|
70
|
+
and the barcode throttle. camera-api.ts - the statics of upstream's class and the
|
|
71
|
+
permission calls. props.ts, picture-options.ts, picture-ref.ts, types.ts.
|
|
72
|
+
src/react/ @symbiote-native/camera/react - CameraView, useCameraPermissions, useMicrophonePermissions
|
|
73
|
+
src/vue/ @symbiote-native/camera/vue - the same names
|
|
74
|
+
src/svelte/ @symbiote-native/camera/svelte - the same names ({ status, requestPermission, getPermission })
|
|
75
|
+
src/solid/ @symbiote-native/camera/solid - the same names
|
|
76
|
+
src/angular/ @symbiote-native/camera/angular - CameraView, CameraPermissionsService, MicrophonePermissionsService
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Use it
|
|
80
|
+
|
|
81
|
+
### React
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { useRef } from 'react';
|
|
85
|
+
import { CameraView, useCameraPermissions, type ICameraViewHandle } from '@symbiote-native/camera/react';
|
|
86
|
+
|
|
87
|
+
const [permission, requestPermission] = useCameraPermissions();
|
|
88
|
+
const camera = useRef<ICameraViewHandle>(null);
|
|
89
|
+
|
|
90
|
+
return permission?.granted === true ? (
|
|
91
|
+
<>
|
|
92
|
+
<CameraView ref={camera} style={{ flex: 1 }} facing="back" />
|
|
93
|
+
<button title="Take picture" onPress={() => camera.current?.takePictureAsync({ quality: 0.8 })} />
|
|
94
|
+
</>
|
|
95
|
+
) : (
|
|
96
|
+
<button title="Allow the camera" onPress={() => requestPermission()} />
|
|
97
|
+
);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Vue
|
|
101
|
+
|
|
102
|
+
```vue
|
|
103
|
+
<script setup lang="ts">
|
|
104
|
+
import { ref } from 'vue';
|
|
105
|
+
import { CameraView, useCameraPermissions, type ICameraViewHandle } from '@symbiote-native/camera/vue';
|
|
106
|
+
|
|
107
|
+
const [permission, requestPermission] = useCameraPermissions();
|
|
108
|
+
const camera = ref<ICameraViewHandle | null>(null);
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<template>
|
|
112
|
+
<template v-if="permission?.granted === true">
|
|
113
|
+
<CameraView ref="camera" :style="{ flex: 1 }" facing="back" />
|
|
114
|
+
<button title="Take picture" @press="camera?.takePictureAsync({ quality: 0.8 })" />
|
|
115
|
+
</template>
|
|
116
|
+
<button v-else title="Allow the camera" @press="requestPermission()" />
|
|
117
|
+
</template>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Angular
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { Component, inject } from '@angular/core';
|
|
124
|
+
import { SYMBIOTE_ELEMENTS } from '@symbiote-native/angular';
|
|
125
|
+
import { CameraPermissionsService, CameraView } from '@symbiote-native/camera/angular';
|
|
126
|
+
|
|
127
|
+
@Component({
|
|
128
|
+
standalone: true,
|
|
129
|
+
imports: [CameraView, SYMBIOTE_ELEMENTS],
|
|
130
|
+
template: `
|
|
131
|
+
@if (permission()?.granted === true) {
|
|
132
|
+
<CameraView #camera [style]="{ flex: 1 }" facing="back" />
|
|
133
|
+
<button title="Take picture" (press)="camera.takePictureAsync({ quality: 0.8 })" />
|
|
134
|
+
} @else {
|
|
135
|
+
<button title="Allow the camera" (press)="request()" />
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
})
|
|
139
|
+
export class CameraCard {
|
|
140
|
+
private readonly service = inject(CameraPermissionsService);
|
|
141
|
+
readonly permission = this.service.connect();
|
|
142
|
+
|
|
143
|
+
request(): void {
|
|
144
|
+
void this.service.request();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Svelte
|
|
150
|
+
|
|
151
|
+
```svelte
|
|
152
|
+
<script lang="ts">
|
|
153
|
+
import { CameraView, useCameraPermissions, type ICameraViewHandle } from '@symbiote-native/camera/svelte';
|
|
154
|
+
|
|
155
|
+
const permission = useCameraPermissions();
|
|
156
|
+
let camera = $state<ICameraViewHandle | undefined>();
|
|
157
|
+
</script>
|
|
158
|
+
|
|
159
|
+
{#if permission.status?.granted === true}
|
|
160
|
+
<CameraView bind:this={camera} style={{ flex: 1 }} facing="back" />
|
|
161
|
+
<button title="Take picture" onPress={() => camera?.takePictureAsync({ quality: 0.8 })} />
|
|
162
|
+
{:else}
|
|
163
|
+
<button title="Allow the camera" onPress={() => permission.requestPermission()} />
|
|
164
|
+
{/if}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Solid
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import { Show, createSignal } from 'solid-js';
|
|
171
|
+
import { CameraView, useCameraPermissions, type ICameraViewHandle } from '@symbiote-native/camera/solid';
|
|
172
|
+
|
|
173
|
+
const [permission, requestPermission] = useCameraPermissions();
|
|
174
|
+
const [camera, setCamera] = createSignal<ICameraViewHandle>();
|
|
175
|
+
|
|
176
|
+
<Show
|
|
177
|
+
when={permission()?.granted === true}
|
|
178
|
+
fallback={<button title="Allow the camera" onPress={() => requestPermission()} />}
|
|
179
|
+
>
|
|
180
|
+
<CameraView ref={setCamera} style={{ flex: 1 }} facing="back" />
|
|
181
|
+
<button title="Take picture" onPress={() => camera()?.takePictureAsync({ quality: 0.8 })} />
|
|
182
|
+
</Show>;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## API
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
<CameraView facing? flash? mode? zoom? mute? enableTorch? active? // active is iOS only
|
|
189
|
+
barcodeScannerSettings? onBarcodeScanned? onCameraReady? onMountError? ... />
|
|
190
|
+
// handle: takePictureAsync, recordAsync, toggleRecordingAsync, stopRecording, pausePreview,
|
|
191
|
+
// resumePreview, getAvailablePictureSizesAsync, getAvailableLensesAsync, getSupportedFeatures
|
|
192
|
+
|
|
193
|
+
isCameraAvailableAsync(): Promise<boolean> // was CameraView.isAvailableAsync
|
|
194
|
+
getAvailableVideoCodecsAsync(): Promise<ICameraVideoCodec[]> // iOS
|
|
195
|
+
launchScanner(options?): Promise<void> dismissScanner(): Promise<void>
|
|
196
|
+
onModernBarcodeScanned(listener): EventSubscription isModernBarcodeScannerAvailable(): boolean
|
|
197
|
+
scanFromURLAsync(url, barcodeTypes = ['qr']): Promise<ICameraBarcodeScanningResult[]>
|
|
198
|
+
Camera.getCameraPermissionsAsync() and the three twins // request and microphone variants
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`PictureRef` keeps its name. Ported from upstream's `CameraView.tsx`, `Camera.ts` and `Camera.types.ts`.
|
|
202
|
+
|
|
203
|
+
## Notes
|
|
204
|
+
|
|
205
|
+
- **The postinstall asks for both permissions.** It writes `NSCameraUsageDescription` and
|
|
206
|
+
`NSMicrophoneUsageDescription` into `Info.plist` and `CAMERA` and `RECORD_AUDIO` into the Android
|
|
207
|
+
manifest. iOS needs the microphone text even if you only take pictures, because the same view can
|
|
208
|
+
record. Reword both texts to fit your app.
|
|
209
|
+
- **Ask first, then render.** Show `CameraView` only once `useCameraPermissions` reports `granted`.
|
|
210
|
+
- **`onBarcodeScanned` turns scanning on.** The scanner is enabled exactly when you pass the prop,
|
|
211
|
+
and `barcodeScannerSettings={{ barcodeTypes: [...] }}` narrows what it looks for.
|
|
212
|
+
- **`CameraView` takes no children.** The native view does not host them, so draw controls and
|
|
213
|
+
frames as siblings, absolutely positioned over it.
|
|
214
|
+
- **The handle works after `onCameraReady`.** `takePictureAsync` and `recordAsync` before that can
|
|
215
|
+
fail or return nothing.
|
|
216
|
+
- **`active={false}` stops the camera session without unmounting the view.** iOS only, it is on by
|
|
217
|
+
default.
|
|
218
|
+
- **Not ported.** The web build (`.web` files, the browser barcode detector) and the plugin's `barcodeScannerEnabled: false` switch, which only trims the native binary size. The only upstream test covers the web manager, the suites here are written against the native behavior.
|
|
219
|
+
|
|
220
|
+
## Common questions
|
|
221
|
+
|
|
222
|
+
- **iOS crash when the camera opens.** A missing usage description. The postinstall writes
|
|
223
|
+
`NSCameraUsageDescription` and `NSMicrophoneUsageDescription`, iOS wants both for the camera view.
|
|
224
|
+
- **Black preview.** Render the view only when permission is `granted`, test on a device (the iOS
|
|
225
|
+
Simulator has no camera) and wait for `onCameraReady` before calling the handle.
|
|
226
|
+
- **The barcode callback.** Passing `onBarcodeScanned` turns the scanner on. To scan once, pass
|
|
227
|
+
`undefined` after the first result.
|
|
228
|
+
- **Children.** `CameraView` takes none, upstream says to overlay a sibling with absolute
|
|
229
|
+
positioning.
|
|
230
|
+
- **Mounting and unmounting freezes or crashes iOS.** Keep the view mounted and set `active={false}`
|
|
231
|
+
(iOS) to stop the session.
|
|
232
|
+
- **Torch turns off at once.** Set `enableTorch` after `onCameraReady`.
|
|
233
|
+
- **System scanner.** `launchScanner()`, iOS 16+ and Google's code scanner on Android.
|
|
234
|
+
|
|
235
|
+
Sources: [Expo docs: Camera](https://docs.expo.dev/versions/latest/sdk/camera/),
|
|
236
|
+
[expo/expo#11736](https://github.com/expo/expo/issues/11736),
|
|
237
|
+
[expo/expo#16126](https://github.com/expo/expo/issues/16126),
|
|
238
|
+
[expo/expo#37150](https://github.com/expo/expo/issues/37150),
|
|
239
|
+
[expo/expo#34957](https://github.com/expo/expo/issues/34957),
|
|
240
|
+
[expo/expo#48780](https://github.com/expo/expo/issues/48780),
|
|
241
|
+
[expo/expo#36352](https://github.com/expo/expo/issues/36352).
|
|
242
|
+
|
|
243
|
+
## Test it
|
|
244
|
+
|
|
245
|
+
No device is needed for the logic. The core tests (`src/core/*.test.ts`) replace `expo-modules-core`
|
|
246
|
+
(`requireNativeViewManager`, `Platform`) and assert the descriptor a render function returns. The
|
|
247
|
+
adapter tests (`src/{react,vue,solid,angular}/**/*.test.*`, `vitest`) render the real component over the
|
|
248
|
+
recording Fabric with an injected view config and assert the committed payload, and Svelte's
|
|
249
|
+
`*.smoke.test.ts` compile the `.svelte` files and mount them. Painting itself is verified on a device in
|
|
250
|
+
the six `examples/expo-*` canary apps (`examples/expo-react`, `examples/expo-vue-sfc`,
|
|
251
|
+
`examples/expo-vue-tsx`, `examples/expo-svelte`, `examples/expo-solid`, `examples/expo-angular`), see the
|
|
252
|
+
parent [README](../../README.md).
|
|
253
|
+
|
|
254
|
+
The permission hooks are tested with a fake native module (`use-camera-permissions.test.*`, the Angular
|
|
255
|
+
service test), the same pattern as every permission package.
|
|
@@ -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,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,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 {};
|