@symbiote-native/image-picker 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 +188 -0
- package/build/angular/index.d.ts +3 -0
- package/build/angular/index.js +3 -0
- package/build/angular/services/camera-permissions.service/index.d.ts +5 -0
- package/build/angular/services/camera-permissions.service/index.js +60 -0
- package/build/angular/services/media-library-permissions.service/index.d.ts +5 -0
- package/build/angular/services/media-library-permissions.service/index.js +60 -0
- package/build/core/image-picker.d.ts +9 -0
- package/build/core/image-picker.js +52 -0
- package/build/core/index.d.ts +3 -0
- package/build/core/index.js +3 -0
- package/build/core/media-library-permission-api.d.ts +11 -0
- package/build/core/media-library-permission-api.js +9 -0
- package/build/core/native-module.d.ts +13 -0
- package/build/core/native-module.js +3 -0
- package/build/core/types.d.ts +127 -0
- package/build/core/types.js +54 -0
- package/build/core/utils.d.ts +4 -0
- package/build/core/utils.js +24 -0
- package/build/react/index.d.ts +3 -0
- package/build/react/index.js +3 -0
- package/build/react/use-camera-permissions.d.ts +8 -0
- package/build/react/use-camera-permissions.js +10 -0
- package/build/react/use-media-library-permissions.d.ts +10 -0
- package/build/react/use-media-library-permissions.js +7 -0
- package/build/solid/index.d.ts +3 -0
- package/build/solid/index.js +3 -0
- package/build/solid/use-camera-permissions.d.ts +9 -0
- package/build/solid/use-camera-permissions.js +10 -0
- package/build/solid/use-media-library-permissions.d.ts +11 -0
- package/build/solid/use-media-library-permissions.js +7 -0
- package/build/svelte/index.d.ts +3 -0
- package/build/svelte/index.js +3 -0
- package/build/svelte/use-camera-permissions.svelte.d.ts +8 -0
- package/build/svelte/use-camera-permissions.svelte.js +10 -0
- package/build/svelte/use-media-library-permissions.svelte.d.ts +10 -0
- package/build/svelte/use-media-library-permissions.svelte.js +7 -0
- package/build/vue/index.d.ts +3 -0
- package/build/vue/index.js +3 -0
- package/build/vue/use-camera-permissions.d.ts +9 -0
- package/build/vue/use-camera-permissions.js +10 -0
- package/build/vue/use-media-library-permissions.d.ts +11 -0
- package/build/vue/use-media-library-permissions.js +7 -0
- package/build-ngc/angular/index.d.ts +3 -0
- package/build-ngc/angular/index.js +4 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/camera-permissions.service/index.d.ts +8 -0
- package/build-ngc/angular/services/camera-permissions.service/index.js +17 -0
- package/build-ngc/angular/services/camera-permissions.service/index.js.map +1 -0
- package/build-ngc/angular/services/media-library-permissions.service/index.d.ts +8 -0
- package/build-ngc/angular/services/media-library-permissions.service/index.js +17 -0
- package/build-ngc/angular/services/media-library-permissions.service/index.js.map +1 -0
- package/build-ngc/core/image-picker.d.ts +9 -0
- package/build-ngc/core/image-picker.js +53 -0
- package/build-ngc/core/image-picker.js.map +1 -0
- package/build-ngc/core/index.d.ts +3 -0
- package/build-ngc/core/index.js +4 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/media-library-permission-api.d.ts +11 -0
- package/build-ngc/core/media-library-permission-api.js +10 -0
- package/build-ngc/core/media-library-permission-api.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +13 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +127 -0
- package/build-ngc/core/types.js +55 -0
- package/build-ngc/core/types.js.map +1 -0
- package/build-ngc/core/utils.d.ts +4 -0
- package/build-ngc/core/utils.js +25 -0
- package/build-ngc/core/utils.js.map +1 -0
- package/native-link.json +20 -0
- package/package.json +150 -0
- package/src/angular/index.ts +3 -0
- package/src/angular/services/camera-permissions.service/index.ts +16 -0
- package/src/angular/services/media-library-permissions.service/index.ts +19 -0
- package/src/core/image-picker.ts +90 -0
- package/src/core/index.ts +11 -0
- package/src/core/media-library-permission-api.ts +23 -0
- package/src/core/native-module.ts +34 -0
- package/src/core/types.ts +145 -0
- package/src/core/utils.ts +36 -0
- package/src/react/index.ts +6 -0
- package/src/react/use-camera-permissions.ts +25 -0
- package/src/react/use-media-library-permissions.ts +26 -0
- package/src/solid/index.ts +6 -0
- package/src/solid/use-camera-permissions.ts +26 -0
- package/src/solid/use-media-library-permissions.ts +27 -0
- package/src/svelte/index.ts +6 -0
- package/src/svelte/svelte-ambient.d.ts +3 -0
- package/src/svelte/svelte-compile.test-helper.ts +7 -0
- package/src/svelte/use-camera-permissions.svelte.ts +25 -0
- package/src/svelte/use-media-library-permissions.svelte.ts +26 -0
- package/src/vue/index.ts +6 -0
- package/src/vue/use-camera-permissions.ts +26 -0
- package/src/vue/use-media-library-permissions.ts +27 -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
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# @symbiote-native/image-picker
|
|
2
|
+
|
|
3
|
+
Let users choose a photo or video from their library, or take one with the camera. One API for
|
|
4
|
+
every [SymbioteNative](../../README.md) adapter (React, Vue, Svelte, Solid and Angular).
|
|
5
|
+
|
|
6
|
+
It wraps [`expo-image-picker`](https://github.com/expo/expo/tree/main/packages/expo-image-picker),
|
|
7
|
+
which opens the system picker UI, the same way
|
|
8
|
+
[`@symbiote-native/document-picker`](../document-picker) wraps its upstream: `expo-modules-core` is
|
|
9
|
+
a direct dependency, never the `expo` meta-package, and the upstream JS is hand-ported into
|
|
10
|
+
`core/`. The mechanics live in the `symbiote-expo-native-module` project skill.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
**New app:**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @symbiote-native/cli new my-app --image-picker
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Existing SymbioteNative app:**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx @symbiote-native/cli add --image-picker
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Either way: installs `@symbiote-native/image-picker` and wires the native autolinking
|
|
27
|
+
automatically - see [`@symbiote-native/cli`](../cli).
|
|
28
|
+
|
|
29
|
+
<details>
|
|
30
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @symbiote-native/image-picker
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`expo-image-picker` and `expo-modules-core` come along as regular, pinned dependencies - never
|
|
37
|
+
install either yourself, and never add the `expo` meta-package to this project (it bundles its
|
|
38
|
+
own Metro/Babel pipeline that conflicts with this project's own).
|
|
39
|
+
|
|
40
|
+
### Required one-time step: native autolinking wiring
|
|
41
|
+
|
|
42
|
+
Unlike a plain RN native module, `expo-image-picker`'s native code is discovered by
|
|
43
|
+
`expo-modules-autolinking` - this needs wiring into the native host app **once**, covering this
|
|
44
|
+
package and every other `expo-modules-core` package with zero further changes:
|
|
45
|
+
|
|
46
|
+
| Platform | Touches |
|
|
47
|
+
| -------- | ------------------------------------------------------------------------------------- |
|
|
48
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
49
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
50
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
51
|
+
| Android | `MainApplication.kt` - Expo's bootstrap hook, plus a native-module name map |
|
|
52
|
+
|
|
53
|
+
Full mechanics live in the `symbiote-expo-native-module` skill. The per-package half of that
|
|
54
|
+
table - the Gradle dependency and the module map entry - is generated by
|
|
55
|
+
[`@symbiote-native/expo-modules-link`](../expo-modules-link) from this package's
|
|
56
|
+
`native-link.json` on every install.
|
|
57
|
+
|
|
58
|
+
### Permissions this package requests
|
|
59
|
+
|
|
60
|
+
- **iOS `Info.plist` keys** (generated automatically): `NSPhotoLibraryUsageDescription`,
|
|
61
|
+
`NSCameraUsageDescription`, `NSMicrophoneUsageDescription` - reword the strings in
|
|
62
|
+
`native-link.json` if the defaults don't fit your app.
|
|
63
|
+
- **Android `RECORD_AUDIO`** (generated automatically) - needed because the camera picker can
|
|
64
|
+
record video with sound. `CAMERA`, `READ_MEDIA_IMAGES`, `READ_MEDIA_VIDEO`, and the pre-33
|
|
65
|
+
storage permissions ship in `expo-image-picker`'s own `AndroidManifest.xml` and merge in
|
|
66
|
+
automatically once the Gradle project is included - nothing to declare for those.
|
|
67
|
+
|
|
68
|
+
</details>
|
|
69
|
+
|
|
70
|
+
## Shape
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
src/core/ launchCameraAsync + launchImageLibraryAsync + getPendingResultAsync +
|
|
74
|
+
the permission functions + the shared permission-hook runtime.
|
|
75
|
+
native-module.ts resolves ExponentImagePicker through
|
|
76
|
+
expo-modules-core's requireNativeModule.
|
|
77
|
+
src/react/ useCameraPermissions + useMediaLibraryPermissions (hooks)
|
|
78
|
+
src/vue/ useCameraPermissions + useMediaLibraryPermissions (composables)
|
|
79
|
+
src/solid/ useCameraPermissions + useMediaLibraryPermissions (primitives)
|
|
80
|
+
src/svelte/ useCameraPermissions + useMediaLibraryPermissions (runes)
|
|
81
|
+
src/angular/ CameraPermissionsService + MediaLibraryPermissionsService (DI)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`launchCameraAsync`/`launchImageLibraryAsync`/`getPendingResultAsync` stay framework-agnostic free
|
|
85
|
+
functions, re-exported by every adapter's own barrel. Upstream's `useCameraPermissions`/
|
|
86
|
+
`useMediaLibraryPermissions` are ported to **every** adapter, not just React: React/Vue/Solid
|
|
87
|
+
bind `createPermissionHook` from `@symbiote-native/{react,vue,solid}` (the method-dispatch
|
|
88
|
+
plumbing lives once in `@symbiote-native/engine`'s `permission-hook-runtime.ts`), Svelte binds it
|
|
89
|
+
from `@symbiote-native/svelte/runes/create-permission-hook`, Angular ships
|
|
90
|
+
`CameraPermissionsService`/`MediaLibraryPermissionsService`.
|
|
91
|
+
|
|
92
|
+
## Use it
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { launchImageLibraryAsync, requestMediaLibraryPermissionsAsync } from '@symbiote-native/image-picker';
|
|
96
|
+
|
|
97
|
+
await requestMediaLibraryPermissionsAsync();
|
|
98
|
+
const result = await launchImageLibraryAsync({ mediaTypes: 'images', quality: 0.8 });
|
|
99
|
+
if (!result.canceled) {
|
|
100
|
+
console.log(result.assets[0].uri);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Taking a photo:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { launchCameraAsync, requestCameraPermissionsAsync } from '@symbiote-native/image-picker';
|
|
108
|
+
|
|
109
|
+
await requestCameraPermissionsAsync();
|
|
110
|
+
const result = await launchCameraAsync({ allowsEditing: true });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## API
|
|
114
|
+
|
|
115
|
+
| Export | Signature | Notes |
|
|
116
|
+
| ------------------------------------ | -------------------------------------------------------------------- | ------------------------------------------ |
|
|
117
|
+
| `launchCameraAsync` | `(options?: IImagePickerOptions) => Promise<IImagePickerResult>` | Throws `UnavailabilityError` if absent. |
|
|
118
|
+
| `launchImageLibraryAsync` | `(options?: IImagePickerOptions) => Promise<IImagePickerResult>` | |
|
|
119
|
+
| `getCameraPermissionsAsync` | `() => Promise<ICameraPermissionResponse>` | |
|
|
120
|
+
| `requestCameraPermissionsAsync` | `() => Promise<ICameraPermissionResponse>` | |
|
|
121
|
+
| `getMediaLibraryPermissionsAsync` | `(writeOnly?: boolean) => Promise<IMediaLibraryPermissionResponse>` | |
|
|
122
|
+
| `requestMediaLibraryPermissionsAsync` | `(writeOnly?: boolean) => Promise<IMediaLibraryPermissionResponse>` | |
|
|
123
|
+
| `getPendingResultAsync` | `() => Promise<IImagePickerResult \| IImagePickerErrorResult \| null>` | Android only - resolves `null` on iOS. |
|
|
124
|
+
|
|
125
|
+
`IImagePickerOptions` carries the full upstream surface: `allowsEditing`, `aspect`, `shape`,
|
|
126
|
+
`quality`, `mediaTypes`, `exif`, `base64`, `videoExportPreset`, `videoQuality`,
|
|
127
|
+
`allowsMultipleSelection`, `selectionLimit`, `orderedSelection`, `defaultTab`, `videoMaxDuration`,
|
|
128
|
+
`presentationStyle`, `cameraType`, `preferredAssetRepresentationMode`, `legacy`,
|
|
129
|
+
`shouldDownloadFromNetwork`.
|
|
130
|
+
|
|
131
|
+
## Declarative permission hooks
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
useCameraPermissions() -> [status, requestPermission, getPermission] (React/Vue/Solid)
|
|
135
|
+
useMediaLibraryPermissions({ writeOnly? }) -> same shape, plus writeOnly forwarding
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
React/Vue/Solid match upstream's tuple return; Svelte returns `{ status, requestPermission,
|
|
139
|
+
getPermission }` (a boxed getter, matching this repo's own rune idiom); Angular exposes
|
|
140
|
+
`CameraPermissionsService`/`MediaLibraryPermissionsService` (`connect()` returns a signal,
|
|
141
|
+
`get()`/`request()` are imperative), same DI shape as `@symbiote-native/brightness`'s own
|
|
142
|
+
`PermissionsService`.
|
|
143
|
+
|
|
144
|
+
## Notes
|
|
145
|
+
|
|
146
|
+
- **`allowsEditing` + `allowsMultipleSelection` together only warns**, matching upstream: the
|
|
147
|
+
native side silently ignores `allowsEditing` in that case rather than throwing.
|
|
148
|
+
- **Deprecated `MediaTypeOptions` enum still works** (ported for parity) but logs a console
|
|
149
|
+
warning pointing at the string/array `mediaTypes` form upstream now prefers.
|
|
150
|
+
- **Check `canceled` before reading `assets`.** The result is a union: `assets` is `null` when the
|
|
151
|
+
user backs out.
|
|
152
|
+
- **Android can kill your app while the camera is open.** Call `getPendingResultAsync()` on launch to
|
|
153
|
+
recover a result that arrived while the activity was destroyed.
|
|
154
|
+
- **Web-only fields dropped**: `ImagePickerAsset.file`, `ImagePickerResult.output`, and the
|
|
155
|
+
`base64`/`capture` fields of the internal web-only options type - this package targets
|
|
156
|
+
iOS/Android only, matching `expo-module.config.json`'s `platforms: ["apple", "android"]`.
|
|
157
|
+
|
|
158
|
+
## Common questions
|
|
159
|
+
|
|
160
|
+
- **Permission before opening the library?** None needed to launch it. Ask only when needed, for
|
|
161
|
+
example for videos on iOS when `allowsEditing` is `false` and the export preset is the default.
|
|
162
|
+
- **Camera does not open on the iOS Simulator.** It has no camera; use a device.
|
|
163
|
+
- **Android: result never arrives after the camera.** Android can destroy the activity; call
|
|
164
|
+
`getPendingResultAsync()` on launch.
|
|
165
|
+
- **Smaller file.** Lower `quality`, and resize with
|
|
166
|
+
[`@symbiote-native/image-manipulator`](../image-manipulator).
|
|
167
|
+
- **Base64 or EXIF.** `base64: true` / `exif: true` add them to each asset.
|
|
168
|
+
- **Wrong crop rectangle on iOS.** An upstream `UIImagePickerController` bug.
|
|
169
|
+
|
|
170
|
+
Sources: [Expo docs: ImagePicker](https://docs.expo.dev/versions/latest/sdk/imagepicker/),
|
|
171
|
+
[expo/expo#20482](https://github.com/expo/expo/issues/20482),
|
|
172
|
+
[expo/expo#11752](https://github.com/expo/expo/issues/11752).
|
|
173
|
+
|
|
174
|
+
## Test it
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pnpm vitest run packages/image-picker
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The core tests fake the native module in place of `requireNativeModule`'s runtime resolution -
|
|
181
|
+
`ExponentImagePicker` only exists on a device, so a headless run would otherwise throw at import.
|
|
182
|
+
Two cases are ported directly from upstream's `src/__tests__/ImagePicker-test.native.ts`
|
|
183
|
+
(`launchCameraAsync`/`launchImageLibraryAsync` default to an empty options object); the rest is
|
|
184
|
+
net-new coverage of option validation, permission delegation, and the Android-only
|
|
185
|
+
`getPendingResultAsync` branch. Upstream's other test suite
|
|
186
|
+
(`plugin/src/__tests__/withImagePicker-test.ts`) exercises the config-plugin's own internals -
|
|
187
|
+
build-time code this project doesn't carry over (native config here is static
|
|
188
|
+
`native-link.json`, not a plugin we run), so it isn't ported.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Angular twin of `../../react`'s `useCameraPermissions`
|
|
2
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
+
var _, done = false;
|
|
8
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
+
var context = {};
|
|
10
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
+
if (kind === "accessor") {
|
|
15
|
+
if (result === void 0) continue;
|
|
16
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
+
}
|
|
21
|
+
else if (_ = accept(result)) {
|
|
22
|
+
if (kind === "field") initializers.unshift(_);
|
|
23
|
+
else descriptor[key] = _;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
+
done = true;
|
|
28
|
+
};
|
|
29
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
+
var useValue = arguments.length > 2;
|
|
31
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
+
}
|
|
34
|
+
return useValue ? value : void 0;
|
|
35
|
+
};
|
|
36
|
+
import { Injectable } from '@angular/core';
|
|
37
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
38
|
+
import { getCameraPermissionsAsync, requestCameraPermissionsAsync, } from '../../../core/index.js';
|
|
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(getCameraPermissionsAsync, requestCameraPermissionsAsync);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return CameraPermissionsService = _classThis;
|
|
59
|
+
})();
|
|
60
|
+
export { CameraPermissionsService };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
2
|
+
import type { IMediaLibraryPermissionResponse } from '../../../core';
|
|
3
|
+
export declare class MediaLibraryPermissionsService extends PermissionsServiceBase<IMediaLibraryPermissionResponse, boolean> {
|
|
4
|
+
constructor();
|
|
5
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Angular twin of `../../react`'s `useMediaLibraryPermissions`, `get`/`request` take a `writeOnly` flag
|
|
2
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
+
var _, done = false;
|
|
8
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
+
var context = {};
|
|
10
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
+
if (kind === "accessor") {
|
|
15
|
+
if (result === void 0) continue;
|
|
16
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
+
}
|
|
21
|
+
else if (_ = accept(result)) {
|
|
22
|
+
if (kind === "field") initializers.unshift(_);
|
|
23
|
+
else descriptor[key] = _;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
+
done = true;
|
|
28
|
+
};
|
|
29
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
+
var useValue = arguments.length > 2;
|
|
31
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
+
}
|
|
34
|
+
return useValue ? value : void 0;
|
|
35
|
+
};
|
|
36
|
+
import { Injectable } from '@angular/core';
|
|
37
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
38
|
+
import { getMediaLibraryPermissionsAsync, requestMediaLibraryPermissionsAsync, } from '../../../core/index.js';
|
|
39
|
+
let MediaLibraryPermissionsService = (() => {
|
|
40
|
+
let _classDecorators = [Injectable({ providedIn: 'root' })];
|
|
41
|
+
let _classDescriptor;
|
|
42
|
+
let _classExtraInitializers = [];
|
|
43
|
+
let _classThis;
|
|
44
|
+
let _classSuper = PermissionsServiceBase;
|
|
45
|
+
var MediaLibraryPermissionsService = 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
|
+
MediaLibraryPermissionsService = _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(getMediaLibraryPermissionsAsync, requestMediaLibraryPermissionsAsync);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return MediaLibraryPermissionsService = _classThis;
|
|
59
|
+
})();
|
|
60
|
+
export { MediaLibraryPermissionsService };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ICameraPermissionResponse, IImagePickerErrorResult, IImagePickerOptions, IImagePickerResult, IMediaLibraryPermissionResponse } from './types';
|
|
2
|
+
export declare function getCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
|
|
3
|
+
export declare function getMediaLibraryPermissionsAsync(writeOnly?: boolean): Promise<IMediaLibraryPermissionResponse>;
|
|
4
|
+
export declare function requestCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
|
|
5
|
+
export declare function requestMediaLibraryPermissionsAsync(writeOnly?: boolean): Promise<IMediaLibraryPermissionResponse>;
|
|
6
|
+
/** Android only — recovers a result lost to `MainActivity` being killed mid-pick. */
|
|
7
|
+
export declare function getPendingResultAsync(): Promise<IImagePickerResult | IImagePickerErrorResult | null>;
|
|
8
|
+
export declare function launchCameraAsync(options?: IImagePickerOptions): Promise<IImagePickerResult>;
|
|
9
|
+
export declare function launchImageLibraryAsync(options?: IImagePickerOptions): Promise<IImagePickerResult>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { CodedError, UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import { expoImagePicker } from './native-module.js';
|
|
3
|
+
import { mapDeprecatedOptions } from './utils.js';
|
|
4
|
+
function validateOptions(options) {
|
|
5
|
+
const { aspect, quality, videoMaxDuration } = options;
|
|
6
|
+
if (aspect != null) {
|
|
7
|
+
const [x, y] = aspect;
|
|
8
|
+
if (x <= 0 || y <= 0) {
|
|
9
|
+
throw new CodedError('ERR_INVALID_ARGUMENT', `Invalid aspect ratio values ${x}:${y}. Provide positive numbers.`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
if (quality != null && (quality < 0 || quality > 1)) {
|
|
13
|
+
throw new CodedError('ERR_INVALID_ARGUMENT', `Invalid 'quality' value ${quality}. Provide a value between 0 and 1.`);
|
|
14
|
+
}
|
|
15
|
+
if (videoMaxDuration != null && videoMaxDuration < 0) {
|
|
16
|
+
throw new CodedError('ERR_INVALID_ARGUMENT', `Invalid 'videoMaxDuration' value ${videoMaxDuration}. Provide a non-negative number.`);
|
|
17
|
+
}
|
|
18
|
+
return options;
|
|
19
|
+
}
|
|
20
|
+
export async function getCameraPermissionsAsync() {
|
|
21
|
+
return expoImagePicker.getCameraPermissionsAsync();
|
|
22
|
+
}
|
|
23
|
+
export async function getMediaLibraryPermissionsAsync(writeOnly = false) {
|
|
24
|
+
return expoImagePicker.getMediaLibraryPermissionsAsync(writeOnly);
|
|
25
|
+
}
|
|
26
|
+
export async function requestCameraPermissionsAsync() {
|
|
27
|
+
return expoImagePicker.requestCameraPermissionsAsync();
|
|
28
|
+
}
|
|
29
|
+
export async function requestMediaLibraryPermissionsAsync(writeOnly = false) {
|
|
30
|
+
return expoImagePicker.requestMediaLibraryPermissionsAsync(writeOnly);
|
|
31
|
+
}
|
|
32
|
+
/** Android only — recovers a result lost to `MainActivity` being killed mid-pick. */
|
|
33
|
+
export async function getPendingResultAsync() {
|
|
34
|
+
if (expoImagePicker.getPendingResultAsync) {
|
|
35
|
+
return expoImagePicker.getPendingResultAsync();
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
export async function launchCameraAsync(options = {}) {
|
|
40
|
+
if (!expoImagePicker.launchCameraAsync) {
|
|
41
|
+
throw new UnavailabilityError('ImagePicker', 'launchCameraAsync');
|
|
42
|
+
}
|
|
43
|
+
const mappedOptions = mapDeprecatedOptions(options);
|
|
44
|
+
return await expoImagePicker.launchCameraAsync(validateOptions(mappedOptions));
|
|
45
|
+
}
|
|
46
|
+
export async function launchImageLibraryAsync(options = {}) {
|
|
47
|
+
const mappedOptions = mapDeprecatedOptions(options);
|
|
48
|
+
if (mappedOptions.allowsEditing && mappedOptions.allowsMultipleSelection) {
|
|
49
|
+
console.warn('[image-picker] `allowsEditing` is ignored when `allowsMultipleSelection` is enabled.');
|
|
50
|
+
}
|
|
51
|
+
return await expoImagePicker.launchImageLibraryAsync(mappedOptions);
|
|
52
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { PermissionStatus } from 'expo-modules-core';
|
|
2
|
+
export { getCameraPermissionsAsync, getMediaLibraryPermissionsAsync, getPendingResultAsync, launchCameraAsync, launchImageLibraryAsync, requestCameraPermissionsAsync, requestMediaLibraryPermissionsAsync, } from './image-picker';
|
|
3
|
+
export * from './types';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { PermissionStatus } from 'expo-modules-core';
|
|
2
|
+
export { getCameraPermissionsAsync, getMediaLibraryPermissionsAsync, getPendingResultAsync, launchCameraAsync, launchImageLibraryAsync, requestCameraPermissionsAsync, requestMediaLibraryPermissionsAsync, } from './image-picker.js';
|
|
3
|
+
export * from './types.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IMediaLibraryPermissionResponse } from './types';
|
|
2
|
+
import { splitWriteOnlyPermissionOptions } from '@symbiote-native/engine';
|
|
3
|
+
export type IMediaLibraryPermissionMethodOptions = {
|
|
4
|
+
writeOnly?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const mediaLibraryPermissionMethods: {
|
|
7
|
+
getMethod: (options?: IMediaLibraryPermissionMethodOptions) => Promise<IMediaLibraryPermissionResponse>;
|
|
8
|
+
requestMethod: (options?: IMediaLibraryPermissionMethodOptions) => Promise<IMediaLibraryPermissionResponse>;
|
|
9
|
+
};
|
|
10
|
+
export declare const splitMediaLibraryPermissionOptions: typeof splitWriteOnlyPermissionOptions;
|
|
11
|
+
export type { IMediaLibraryPermissionResponse };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// `methods` every adapter's `useMediaLibraryPermissions` binds to the shared
|
|
2
|
+
// `@symbiote-native/{react,vue,solid}` `createPermissionHook` factory
|
|
3
|
+
import { getMediaLibraryPermissionsAsync, requestMediaLibraryPermissionsAsync, } from './index.js';
|
|
4
|
+
import { splitWriteOnlyPermissionOptions } from '@symbiote-native/engine';
|
|
5
|
+
export const mediaLibraryPermissionMethods = {
|
|
6
|
+
getMethod: (options) => getMediaLibraryPermissionsAsync(options?.writeOnly),
|
|
7
|
+
requestMethod: (options) => requestMediaLibraryPermissionsAsync(options?.writeOnly),
|
|
8
|
+
};
|
|
9
|
+
export const splitMediaLibraryPermissionOptions = splitWriteOnlyPermissionOptions;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ICameraPermissionResponse, IImagePickerErrorResult, IImagePickerOptions, IImagePickerResult, IMediaLibraryPermissionResponse } from './types';
|
|
2
|
+
export type INativeImagePickerModule = {
|
|
3
|
+
getCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
|
|
4
|
+
getMediaLibraryPermissionsAsync(writeOnly: boolean): Promise<IMediaLibraryPermissionResponse>;
|
|
5
|
+
requestCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
|
|
6
|
+
requestMediaLibraryPermissionsAsync(writeOnly: boolean): Promise<IMediaLibraryPermissionResponse>;
|
|
7
|
+
/** Android only — absent on iOS's native module. */
|
|
8
|
+
getPendingResultAsync?(): Promise<IImagePickerResult | IImagePickerErrorResult>;
|
|
9
|
+
/** Absent on web's native module. */
|
|
10
|
+
launchCameraAsync?(options: IImagePickerOptions): Promise<IImagePickerResult>;
|
|
11
|
+
launchImageLibraryAsync(options: IImagePickerOptions): Promise<IImagePickerResult>;
|
|
12
|
+
};
|
|
13
|
+
export declare const expoImagePicker: INativeImagePickerModule;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { PermissionResponse } from 'expo-modules-core';
|
|
2
|
+
export type ICameraPermissionResponse = PermissionResponse;
|
|
3
|
+
export type IMediaLibraryPermissionResponse = PermissionResponse & {
|
|
4
|
+
/** `'all'` | `'limited'` (Android 13+/iOS 14+) | `'none'`. */
|
|
5
|
+
accessPrivileges?: 'all' | 'limited' | 'none';
|
|
6
|
+
};
|
|
7
|
+
/** @deprecated use an array of `IMediaType` instead. */
|
|
8
|
+
export declare enum MediaTypeOptions {
|
|
9
|
+
All = "All",
|
|
10
|
+
Videos = "Videos",
|
|
11
|
+
Images = "Images"
|
|
12
|
+
}
|
|
13
|
+
export type IMediaType = 'images' | 'videos' | 'livePhotos';
|
|
14
|
+
/** Android only. */
|
|
15
|
+
export type IDefaultTab = 'photos' | 'albums';
|
|
16
|
+
export declare enum VideoExportPreset {
|
|
17
|
+
Passthrough = 0,
|
|
18
|
+
LowQuality = 1,
|
|
19
|
+
MediumQuality = 2,
|
|
20
|
+
HighestQuality = 3,
|
|
21
|
+
H264_640x480 = 4,
|
|
22
|
+
H264_960x540 = 5,
|
|
23
|
+
H264_1280x720 = 6,
|
|
24
|
+
H264_1920x1080 = 7,
|
|
25
|
+
H264_3840x2160 = 8,
|
|
26
|
+
HEVC_1920x1080 = 9,
|
|
27
|
+
HEVC_3840x2160 = 10
|
|
28
|
+
}
|
|
29
|
+
export declare enum UIImagePickerControllerQualityType {
|
|
30
|
+
High = 0,
|
|
31
|
+
Medium = 1,
|
|
32
|
+
Low = 2,
|
|
33
|
+
VGA640x480 = 3,
|
|
34
|
+
IFrame1280x720 = 4,
|
|
35
|
+
IFrame960x540 = 5
|
|
36
|
+
}
|
|
37
|
+
/** iOS only — maps to `UIModalPresentationStyle`. */
|
|
38
|
+
export declare enum UIImagePickerPresentationStyle {
|
|
39
|
+
FULL_SCREEN = "fullScreen",
|
|
40
|
+
PAGE_SHEET = "pageSheet",
|
|
41
|
+
FORM_SHEET = "formSheet",
|
|
42
|
+
CURRENT_CONTEXT = "currentContext",
|
|
43
|
+
OVER_FULL_SCREEN = "overFullScreen",
|
|
44
|
+
OVER_CURRENT_CONTEXT = "overCurrentContext",
|
|
45
|
+
POPOVER = "popover",
|
|
46
|
+
AUTOMATIC = "automatic"
|
|
47
|
+
}
|
|
48
|
+
/** iOS only — maps to `PHPickerConfigurationAssetRepresentationMode`. */
|
|
49
|
+
export declare enum UIImagePickerPreferredAssetRepresentationMode {
|
|
50
|
+
Automatic = "automatic",
|
|
51
|
+
Compatible = "compatible",
|
|
52
|
+
Current = "current"
|
|
53
|
+
}
|
|
54
|
+
export declare enum CameraType {
|
|
55
|
+
back = "back",
|
|
56
|
+
front = "front"
|
|
57
|
+
}
|
|
58
|
+
export type IImagePickerAsset = {
|
|
59
|
+
uri: string;
|
|
60
|
+
/** Usable with `@symbiote-native/media-library`. Null if unavailable or access is limited. */
|
|
61
|
+
assetId?: string | null;
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
type?: 'image' | 'video' | 'livePhoto' | 'pairedVideo' | null;
|
|
65
|
+
fileName?: string | null;
|
|
66
|
+
fileSize?: number;
|
|
67
|
+
exif?: Record<string, unknown> | null;
|
|
68
|
+
/** Base64 JPEG data — set only when the `base64` option is truthy. */
|
|
69
|
+
base64?: string | null;
|
|
70
|
+
duration?: number | null;
|
|
71
|
+
mimeType?: string;
|
|
72
|
+
/** Set only when `livePhotos` was requested and a live photo was picked. iOS only. */
|
|
73
|
+
pairedVideoAsset?: IImagePickerAsset | null;
|
|
74
|
+
};
|
|
75
|
+
export type IImagePickerErrorResult = {
|
|
76
|
+
code: string;
|
|
77
|
+
message: string;
|
|
78
|
+
exception?: string;
|
|
79
|
+
};
|
|
80
|
+
export type IImagePickerSuccessResult = {
|
|
81
|
+
canceled: false;
|
|
82
|
+
assets: IImagePickerAsset[];
|
|
83
|
+
};
|
|
84
|
+
export type IImagePickerCanceledResult = {
|
|
85
|
+
canceled: true;
|
|
86
|
+
assets: null;
|
|
87
|
+
};
|
|
88
|
+
export type IImagePickerResult = IImagePickerSuccessResult | IImagePickerCanceledResult;
|
|
89
|
+
export type ICropShape = 'rectangle' | 'oval';
|
|
90
|
+
export type IImagePickerOptions = {
|
|
91
|
+
/** Crop/rotate after picking. Exclusive with `allowsMultipleSelection`. Default `false`. */
|
|
92
|
+
allowsEditing?: boolean;
|
|
93
|
+
/** `[x, y]` crop aspect ratio. Android only. */
|
|
94
|
+
aspect?: [number, number];
|
|
95
|
+
/** Android only. Default `'rectangle'`. */
|
|
96
|
+
shape?: ICropShape;
|
|
97
|
+
/** `0`–`1`. Default `1.0`. */
|
|
98
|
+
quality?: number;
|
|
99
|
+
/** Default `'images'`. */
|
|
100
|
+
mediaTypes?: IMediaType | IMediaType[] | MediaTypeOptions;
|
|
101
|
+
exif?: boolean;
|
|
102
|
+
base64?: boolean;
|
|
103
|
+
/** @deprecated iOS 11+ only. */
|
|
104
|
+
videoExportPreset?: VideoExportPreset;
|
|
105
|
+
/** iOS only. Default `High`. */
|
|
106
|
+
videoQuality?: UIImagePickerControllerQualityType;
|
|
107
|
+
/** Ignored when `allowsEditing` is set. Default `false`. */
|
|
108
|
+
allowsMultipleSelection?: boolean;
|
|
109
|
+
/** `0` = system maximum. Android + iOS 14+. Default `0`. */
|
|
110
|
+
selectionLimit?: number;
|
|
111
|
+
/** iOS 15+ only. Default `false`. */
|
|
112
|
+
orderedSelection?: boolean;
|
|
113
|
+
/** Android only. Default `'photos'`. */
|
|
114
|
+
defaultTab?: IDefaultTab;
|
|
115
|
+
/** Seconds. `0` = no limit (default). No effect on web. */
|
|
116
|
+
videoMaxDuration?: number;
|
|
117
|
+
/** iOS only. Default `AUTOMATIC`. */
|
|
118
|
+
presentationStyle?: UIImagePickerPresentationStyle;
|
|
119
|
+
/** Default `back`. */
|
|
120
|
+
cameraType?: CameraType;
|
|
121
|
+
/** iOS 14+ only. Default `Automatic`. */
|
|
122
|
+
preferredAssetRepresentationMode?: UIImagePickerPreferredAssetRepresentationMode;
|
|
123
|
+
/** Android only — allows picking from outside the photo library. Default `false`. */
|
|
124
|
+
legacy?: boolean;
|
|
125
|
+
/** iOS only — download from iCloud if not stored locally. Default `false`. */
|
|
126
|
+
shouldDownloadFromNetwork?: boolean;
|
|
127
|
+
};
|