This commit is contained in:
metacryst
2026-01-09 11:14:27 -06:00
parent cf03c95664
commit 637c9e4674
2149 changed files with 527743 additions and 0 deletions

17
node_modules/@capacitor/camera/CapacitorCamera.podspec generated vendored Normal file
View File

@@ -0,0 +1,17 @@
require 'json'
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
Pod::Spec.new do |s|
s.name = 'CapacitorCamera'
s.version = package['version']
s.summary = package['description']
s.license = package['license']
s.homepage = 'https://capacitorjs.com'
s.author = package['author']
s.source = { :git => 'https://github.com/ionic-team/capacitor-plugins.git', :tag => package['name'] + '@' + package['version'] }
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}', 'camera/ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
s.ios.deployment_target = '14.0'
s.dependency 'Capacitor'
s.swift_version = '5.1'
end

23
node_modules/@capacitor/camera/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,23 @@
Copyright 2020-present Ionic
https://ionic.io
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

28
node_modules/@capacitor/camera/Package.swift generated vendored Normal file
View File

@@ -0,0 +1,28 @@
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "CapacitorCamera",
platforms: [.iOS(.v14)],
products: [
.library(
name: "CapacitorCamera",
targets: ["CameraPlugin"])
],
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0")
],
targets: [
.target(
name: "CameraPlugin",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm")
],
path: "ios/Sources/CameraPlugin"),
.testTarget(
name: "CameraPluginTests",
dependencies: ["CameraPlugin"],
path: "ios/Tests/CameraPluginTests")
]
)

353
node_modules/@capacitor/camera/README.md generated vendored Normal file
View File

@@ -0,0 +1,353 @@
# @capacitor/camera
The Camera API provides the ability to take a photo with the camera or choose an existing one from the photo album.
## Install
```bash
npm install @capacitor/camera
npx cap sync
```
## iOS
iOS requires the following usage description be added and filled out for your app in `Info.plist`:
- `NSCameraUsageDescription` (`Privacy - Camera Usage Description`)
- `NSPhotoLibraryAddUsageDescription` (`Privacy - Photo Library Additions Usage Description`)
- `NSPhotoLibraryUsageDescription` (`Privacy - Photo Library Usage Description`)
Read about [Configuring `Info.plist`](https://capacitorjs.com/docs/ios/configuration#configuring-infoplist) in the [iOS Guide](https://capacitorjs.com/docs/ios) for more information on setting iOS permissions in Xcode
## Android
When picking existing images from the device gallery, the Android Photo Picker component is now used. The Photo Picker is available on devices that meet the following criteria:
- Run Android 11 (API level 30) or higher
- Receive changes to Modular System Components through Google System Updates
Older devices and Android Go devices running Android 11 or 12 that support Google Play services can install a backported version of the photo picker. To enable the automatic installation of the backported photo picker module through Google Play services, add the following entry to the `<application>` tag in your `AndroidManifest.xml` file:
```xml
<!-- Trigger Google Play services to install the backported photo picker module. -->
<!--suppress AndroidDomInspection -->
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
android:enabled="false"
android:exported="false"
tools:ignore="MissingClass">
<intent-filter>
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
</intent-filter>
<meta-data android:name="photopicker_activity:0:required" android:value="" />
</service>
```
If that entry is not added, the devices that don't support the Photo Picker, the Photo Picker component fallbacks to `Intent.ACTION_OPEN_DOCUMENT`.
The Camera plugin requires no permissions, unless using `saveToGallery: true`, in that case the following permissions should be added to your `AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```
You can also specify those permissions only for the Android versions where they will be requested:
```xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/>
```
The storage permissions are for reading/saving photo files.
Read about [Setting Permissions](https://capacitorjs.com/docs/android/configuration#setting-permissions) in the [Android Guide](https://capacitorjs.com/docs/android) for more information on setting Android permissions.
Additionally, because the Camera API launches a separate Activity to handle taking the photo, you should listen for `appRestoredResult` in the `App` plugin to handle any camera data that was sent in the case your app was terminated by the operating system while the Activity was running.
### Variables
This plugin will use the following project variables (defined in your app's `variables.gradle` file):
- `androidxExifInterfaceVersion`: version of `androidx.exifinterface:exifinterface` (default: `1.3.7`)
- `androidxMaterialVersion`: version of `com.google.android.material:material` (default: `1.12.0`)
## PWA Notes
[PWA Elements](https://capacitorjs.com/docs/web/pwa-elements) are required for Camera plugin to work.
## Example
```typescript
import { Camera, CameraResultType } from '@capacitor/camera';
const takePicture = async () => {
const image = await Camera.getPhoto({
quality: 90,
allowEditing: true,
resultType: CameraResultType.Uri
});
// image.webPath will contain a path that can be set as an image src.
// You can access the original file using image.path, which can be
// passed to the Filesystem API to read the raw data of the image,
// if desired (or pass resultType: CameraResultType.Base64 to getPhoto)
var imageUrl = image.webPath;
// Can be set to the src of an image now
imageElement.src = imageUrl;
};
```
## API
<docgen-index>
* [`getPhoto(...)`](#getphoto)
* [`pickImages(...)`](#pickimages)
* [`pickLimitedLibraryPhotos()`](#picklimitedlibraryphotos)
* [`getLimitedLibraryPhotos()`](#getlimitedlibraryphotos)
* [`checkPermissions()`](#checkpermissions)
* [`requestPermissions(...)`](#requestpermissions)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)
* [Enums](#enums)
</docgen-index>
<docgen-api>
<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
### getPhoto(...)
```typescript
getPhoto(options: ImageOptions) => Promise<Photo>
```
Prompt the user to pick a photo from an album, or take a new photo
with the camera.
| Param | Type |
| ------------- | ----------------------------------------------------- |
| **`options`** | <code><a href="#imageoptions">ImageOptions</a></code> |
**Returns:** <code>Promise&lt;<a href="#photo">Photo</a>&gt;</code>
**Since:** 1.0.0
--------------------
### pickImages(...)
```typescript
pickImages(options: GalleryImageOptions) => Promise<GalleryPhotos>
```
Allows the user to pick multiple pictures from the photo gallery.
On iOS 13 and older it only allows to pick one picture.
| Param | Type |
| ------------- | ------------------------------------------------------------------- |
| **`options`** | <code><a href="#galleryimageoptions">GalleryImageOptions</a></code> |
**Returns:** <code>Promise&lt;<a href="#galleryphotos">GalleryPhotos</a>&gt;</code>
**Since:** 1.2.0
--------------------
### pickLimitedLibraryPhotos()
```typescript
pickLimitedLibraryPhotos() => Promise<GalleryPhotos>
```
iOS 14+ Only: Allows the user to update their limited photo library selection.
On iOS 15+ returns all the limited photos after the picker dismissal.
On iOS 14 or if the user gave full access to the photos it returns an empty array.
**Returns:** <code>Promise&lt;<a href="#galleryphotos">GalleryPhotos</a>&gt;</code>
**Since:** 4.1.0
--------------------
### getLimitedLibraryPhotos()
```typescript
getLimitedLibraryPhotos() => Promise<GalleryPhotos>
```
iOS 14+ Only: Return an array of photos selected from the limited photo library.
**Returns:** <code>Promise&lt;<a href="#galleryphotos">GalleryPhotos</a>&gt;</code>
**Since:** 4.1.0
--------------------
### checkPermissions()
```typescript
checkPermissions() => Promise<PermissionStatus>
```
Check camera and photo album permissions
**Returns:** <code>Promise&lt;<a href="#permissionstatus">PermissionStatus</a>&gt;</code>
**Since:** 1.0.0
--------------------
### requestPermissions(...)
```typescript
requestPermissions(permissions?: CameraPluginPermissions | undefined) => Promise<PermissionStatus>
```
Request camera and photo album permissions
| Param | Type |
| ----------------- | --------------------------------------------------------------------------- |
| **`permissions`** | <code><a href="#camerapluginpermissions">CameraPluginPermissions</a></code> |
**Returns:** <code>Promise&lt;<a href="#permissionstatus">PermissionStatus</a>&gt;</code>
**Since:** 1.0.0
--------------------
### Interfaces
#### Photo
| Prop | Type | Description | Since |
| ------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
| **`base64String`** | <code>string</code> | The base64 encoded string representation of the image, if using <a href="#cameraresulttype">CameraResultType.Base64</a>. | 1.0.0 |
| **`dataUrl`** | <code>string</code> | The url starting with 'data:image/jpeg;base64,' and the base64 encoded string representation of the image, if using <a href="#cameraresulttype">CameraResultType.DataUrl</a>. Note: On web, the file format could change depending on the browser. | 1.0.0 |
| **`path`** | <code>string</code> | If using <a href="#cameraresulttype">CameraResultType.Uri</a>, the path will contain a full, platform-specific file URL that can be read later using the Filesystem API. | 1.0.0 |
| **`webPath`** | <code>string</code> | webPath returns a path that can be used to set the src attribute of an image for efficient loading and rendering. | 1.0.0 |
| **`exif`** | <code>any</code> | Exif data, if any, retrieved from the image | 1.0.0 |
| **`format`** | <code>string</code> | The format of the image, ex: jpeg, png, gif. iOS and Android only support jpeg. Web supports jpeg, png and gif, but the exact availability may vary depending on the browser. gif is only supported if `webUseInput` is set to `true` or if `source` is set to `Photos`. | 1.0.0 |
| **`saved`** | <code>boolean</code> | Whether if the image was saved to the gallery or not. On Android and iOS, saving to the gallery can fail if the user didn't grant the required permissions. On Web there is no gallery, so always returns false. | 1.1.0 |
#### ImageOptions
| Prop | Type | Description | Default | Since |
| ------------------------ | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ----- |
| **`quality`** | <code>number</code> | The quality of image to return as JPEG, from 0-100 Note: This option is only supported on Android and iOS | | 1.0.0 |
| **`allowEditing`** | <code>boolean</code> | Whether to allow the user to crop or make small edits (platform specific). On iOS 14+ it's only supported for <a href="#camerasource">CameraSource.Camera</a>, but not for <a href="#camerasource">CameraSource.Photos</a>. | | 1.0.0 |
| **`resultType`** | <code><a href="#cameraresulttype">CameraResultType</a></code> | How the data should be returned. Currently, only 'Base64', 'DataUrl' or 'Uri' is supported | | 1.0.0 |
| **`saveToGallery`** | <code>boolean</code> | Whether to save the photo to the gallery. If the photo was picked from the gallery, it will only be saved if edited. | <code>: false</code> | 1.0.0 |
| **`width`** | <code>number</code> | The desired maximum width of the saved image. The aspect ratio is respected. | | 1.0.0 |
| **`height`** | <code>number</code> | The desired maximum height of the saved image. The aspect ratio is respected. | | 1.0.0 |
| **`correctOrientation`** | <code>boolean</code> | Whether to automatically rotate the image "up" to correct for orientation in portrait mode | <code>: true</code> | 1.0.0 |
| **`source`** | <code><a href="#camerasource">CameraSource</a></code> | The source to get the photo from. By default this prompts the user to select either the photo album or take a photo. | <code>: CameraSource.Prompt</code> | 1.0.0 |
| **`direction`** | <code><a href="#cameradirection">CameraDirection</a></code> | iOS and Web only: The camera direction. | <code>: CameraDirection.Rear</code> | 1.0.0 |
| **`presentationStyle`** | <code>'fullscreen' \| 'popover'</code> | iOS only: The presentation style of the Camera. | <code>: 'fullscreen'</code> | 1.0.0 |
| **`webUseInput`** | <code>boolean</code> | Web only: Whether to use the PWA Element experience or file input. The default is to use PWA Elements if installed and fall back to file input. To always use file input, set this to `true`. Learn more about PWA Elements: https://capacitorjs.com/docs/web/pwa-elements | | 1.0.0 |
| **`promptLabelHeader`** | <code>string</code> | Text value to use when displaying the prompt. | <code>: 'Photo'</code> | 1.0.0 |
| **`promptLabelCancel`** | <code>string</code> | Text value to use when displaying the prompt. iOS only: The label of the 'cancel' button. | <code>: 'Cancel'</code> | 1.0.0 |
| **`promptLabelPhoto`** | <code>string</code> | Text value to use when displaying the prompt. The label of the button to select a saved image. | <code>: 'From Photos'</code> | 1.0.0 |
| **`promptLabelPicture`** | <code>string</code> | Text value to use when displaying the prompt. The label of the button to open the camera. | <code>: 'Take Picture'</code> | 1.0.0 |
#### GalleryPhotos
| Prop | Type | Description | Since |
| ------------ | --------------------------- | ------------------------------- | ----- |
| **`photos`** | <code>GalleryPhoto[]</code> | Array of all the picked photos. | 1.2.0 |
#### GalleryPhoto
| Prop | Type | Description | Since |
| ------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------- | ----- |
| **`path`** | <code>string</code> | Full, platform-specific file URL that can be read later using the Filesystem API. | 1.2.0 |
| **`webPath`** | <code>string</code> | webPath returns a path that can be used to set the src attribute of an image for efficient loading and rendering. | 1.2.0 |
| **`exif`** | <code>any</code> | Exif data, if any, retrieved from the image | 1.2.0 |
| **`format`** | <code>string</code> | The format of the image, ex: jpeg, png, gif. iOS and Android only support jpeg. Web supports jpeg, png and gif. | 1.2.0 |
#### GalleryImageOptions
| Prop | Type | Description | Default | Since |
| ------------------------ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------- | ----- |
| **`quality`** | <code>number</code> | The quality of image to return as JPEG, from 0-100 Note: This option is only supported on Android and iOS. | | 1.2.0 |
| **`width`** | <code>number</code> | The desired maximum width of the saved image. The aspect ratio is respected. | | 1.2.0 |
| **`height`** | <code>number</code> | The desired maximum height of the saved image. The aspect ratio is respected. | | 1.2.0 |
| **`correctOrientation`** | <code>boolean</code> | Whether to automatically rotate the image "up" to correct for orientation in portrait mode | <code>: true</code> | 1.2.0 |
| **`presentationStyle`** | <code>'fullscreen' \| 'popover'</code> | iOS only: The presentation style of the Camera. | <code>: 'fullscreen'</code> | 1.2.0 |
| **`limit`** | <code>number</code> | Maximum number of pictures the user will be able to choose. Note: This option is only supported on Android 13+ and iOS. | <code>0 (unlimited)</code> | 1.2.0 |
#### PermissionStatus
| Prop | Type |
| ------------ | ----------------------------------------------------------------------- |
| **`camera`** | <code><a href="#camerapermissionstate">CameraPermissionState</a></code> |
| **`photos`** | <code><a href="#camerapermissionstate">CameraPermissionState</a></code> |
#### CameraPluginPermissions
| Prop | Type |
| ----------------- | ----------------------------------- |
| **`permissions`** | <code>CameraPermissionType[]</code> |
### Type Aliases
#### CameraPermissionState
<code><a href="#permissionstate">PermissionState</a> | 'limited'</code>
#### PermissionState
<code>'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'</code>
#### CameraPermissionType
<code>'camera' | 'photos'</code>
### Enums
#### CameraResultType
| Members | Value |
| ------------- | ---------------------- |
| **`Uri`** | <code>'uri'</code> |
| **`Base64`** | <code>'base64'</code> |
| **`DataUrl`** | <code>'dataUrl'</code> |
#### CameraSource
| Members | Value | Description |
| ------------ | --------------------- | ------------------------------------------------------------------ |
| **`Prompt`** | <code>'PROMPT'</code> | Prompts the user to select either the photo album or take a photo. |
| **`Camera`** | <code>'CAMERA'</code> | Take a new photo using the camera. |
| **`Photos`** | <code>'PHOTOS'</code> | Pick an existing photo from the gallery or photo album. |
#### CameraDirection
| Members | Value |
| ----------- | -------------------- |
| **`Rear`** | <code>'REAR'</code> |
| **`Front`** | <code>'FRONT'</code> |
</docgen-api>

83
node_modules/@capacitor/camera/android/build.gradle generated vendored Normal file
View File

@@ -0,0 +1,83 @@
ext {
capacitorVersion = System.getenv('CAPACITOR_VERSION')
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
androidxExifInterfaceVersion = project.hasProperty('androidxExifInterfaceVersion') ? rootProject.ext.androidxExifInterfaceVersion : '1.3.7'
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
androidxMaterialVersion = project.hasProperty('androidxMaterialVersion') ? rootProject.ext.androidxMaterialVersion : '1.12.0'
}
buildscript {
repositories {
google()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
if (System.getenv("CAP_PLUGIN_PUBLISH") == "true") {
classpath 'io.github.gradle-nexus:publish-plugin:1.3.0'
}
}
}
apply plugin: 'com.android.library'
if (System.getenv("CAP_PLUGIN_PUBLISH") == "true") {
apply plugin: 'io.github.gradle-nexus.publish-plugin'
apply from: file('../../scripts/android/publish-root.gradle')
apply from: file('../../scripts/android/publish-module.gradle')
}
android {
namespace "com.capacitorjs.plugins.camera"
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
defaultConfig {
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
publishing {
singleVariant("release")
}
}
repositories {
google()
mavenCentral()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
if (System.getenv("CAP_PLUGIN_PUBLISH") == "true") {
implementation "com.capacitorjs:core:$capacitorVersion"
} else {
implementation project(':capacitor-android')
}
implementation "androidx.exifinterface:exifinterface:$androidxExifInterfaceVersion"
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "com.google.android.material:material:$androidxMaterialVersion"
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
}

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
</manifest>

View File

@@ -0,0 +1,124 @@
package com.capacitorjs.plugins.camera;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.view.View;
import android.view.Window;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.util.List;
public class CameraBottomSheetDialogFragment extends BottomSheetDialogFragment {
interface BottomSheetOnSelectedListener {
void onSelected(int index);
}
interface BottomSheetOnCanceledListener {
void onCanceled();
}
private BottomSheetOnSelectedListener selectedListener;
private BottomSheetOnCanceledListener canceledListener;
private List<String> options;
private String title;
void setTitle(String title) {
this.title = title;
}
void setOptions(List<String> options, BottomSheetOnSelectedListener selectedListener, BottomSheetOnCanceledListener canceledListener) {
this.options = options;
this.selectedListener = selectedListener;
this.canceledListener = canceledListener;
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
if (canceledListener != null) {
this.canceledListener.onCanceled();
}
}
private BottomSheetBehavior.BottomSheetCallback mBottomSheetBehaviorCallback = new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {}
};
@Override
@SuppressLint("RestrictedApi")
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
if (options == null || options.size() == 0) {
return;
}
Window w = dialog.getWindow();
final float scale = getResources().getDisplayMetrics().density;
float layoutPaddingDp16 = 16.0f;
float layoutPaddingDp12 = 12.0f;
float layoutPaddingDp8 = 8.0f;
int layoutPaddingPx16 = (int) (layoutPaddingDp16 * scale + 0.5f);
int layoutPaddingPx12 = (int) (layoutPaddingDp12 * scale + 0.5f);
int layoutPaddingPx8 = (int) (layoutPaddingDp8 * scale + 0.5f);
CoordinatorLayout parentLayout = new CoordinatorLayout(getContext());
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(layoutPaddingPx16, layoutPaddingPx16, layoutPaddingPx16, layoutPaddingPx16);
TextView ttv = new TextView(getContext());
ttv.setTextColor(Color.parseColor("#757575"));
ttv.setPadding(layoutPaddingPx8, layoutPaddingPx8, layoutPaddingPx8, layoutPaddingPx8);
ttv.setText(title);
layout.addView(ttv);
for (int i = 0; i < options.size(); i++) {
final int optionIndex = i;
TextView tv = new TextView(getContext());
tv.setTextColor(Color.parseColor("#000000"));
tv.setPadding(layoutPaddingPx12, layoutPaddingPx12, layoutPaddingPx12, layoutPaddingPx12);
tv.setText(options.get(i));
tv.setOnClickListener(
view -> {
if (selectedListener != null) {
selectedListener.onSelected(optionIndex);
}
dismiss();
}
);
layout.addView(tv);
}
parentLayout.addView(layout.getRootView());
dialog.setContentView(parentLayout.getRootView());
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) parentLayout.getParent()).getLayoutParams();
CoordinatorLayout.Behavior<View> behavior = params.getBehavior();
if (behavior instanceof BottomSheetBehavior) {
BottomSheetBehavior<View> bottomSheetBehavior = (BottomSheetBehavior<View>) behavior;
bottomSheetBehavior.addBottomSheetCallback(mBottomSheetBehaviorCallback);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
}

View File

@@ -0,0 +1,923 @@
package com.capacitorjs.plugins.camera;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.util.Base64;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.ActivityResultRegistryOwner;
import androidx.activity.result.PickVisualMediaRequest;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import com.getcapacitor.FileUtils;
import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
import com.getcapacitor.Logger;
import com.getcapacitor.PermissionState;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.ActivityCallback;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.getcapacitor.annotation.Permission;
import com.getcapacitor.annotation.PermissionCallback;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONException;
/**
* The Camera plugin makes it easy to take a photo or have the user select a photo
* from their albums.
*
* On Android, this plugin sends an intent that opens the stock Camera app.
*
* Adapted from https://developer.android.com/training/camera/photobasics.html
*/
@SuppressLint("InlinedApi")
@CapacitorPlugin(
name = "Camera",
permissions = {
@Permission(strings = { Manifest.permission.CAMERA }, alias = CameraPlugin.CAMERA),
@Permission(strings = {}, alias = CameraPlugin.PHOTOS),
// SDK VERSIONS 29 AND BELOW
@Permission(
strings = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE },
alias = CameraPlugin.SAVE_GALLERY
),
/*
SDK VERSIONS 30-32
This alias is a placeholder and the SAVE_GALLERY alias will be updated to use this permission
so that the end user does not need to explicitly use separate aliases depending
on the SDK version.
*/
@Permission(strings = { Manifest.permission.READ_EXTERNAL_STORAGE }, alias = CameraPlugin.READ_EXTERNAL_STORAGE)
}
)
public class CameraPlugin extends Plugin {
// Permission alias constants
static final String CAMERA = "camera";
static final String PHOTOS = "photos";
static final String SAVE_GALLERY = "saveGallery";
static final String READ_EXTERNAL_STORAGE = "readExternalStorage";
// Message constants
private static final String INVALID_RESULT_TYPE_ERROR = "Invalid resultType option";
private static final String PERMISSION_DENIED_ERROR_CAMERA = "User denied access to camera";
private static final String NO_CAMERA_ERROR = "Device doesn't have a camera available";
private static final String NO_CAMERA_ACTIVITY_ERROR = "Unable to resolve camera activity";
private static final String NO_PHOTO_ACTIVITY_ERROR = "Unable to resolve photo activity";
private static final String IMAGE_FILE_SAVE_ERROR = "Unable to create photo on disk";
private static final String IMAGE_PROCESS_NO_FILE_ERROR = "Unable to process image, file not found on disk";
private static final String UNABLE_TO_PROCESS_IMAGE = "Unable to process image";
private static final String IMAGE_EDIT_ERROR = "Unable to edit image";
private static final String IMAGE_GALLERY_SAVE_ERROR = "Unable to save the image in the gallery";
private static final String USER_CANCELLED = "User cancelled photos app";
private String imageFileSavePath;
private String imageEditedFileSavePath;
private Uri imageFileUri;
private Uri imagePickedContentUri;
private boolean isEdited = false;
private boolean isFirstRequest = true;
private boolean isSaved = false;
private ActivityResultLauncher<PickVisualMediaRequest> pickMultipleMedia = null;
private ActivityResultLauncher<PickVisualMediaRequest> pickMedia = null;
private final AtomicInteger mNextLocalRequestCode = new AtomicInteger();
private CameraSettings settings = new CameraSettings();
@Override
public void load() {
super.load();
}
@PluginMethod
public void getPhoto(PluginCall call) {
isEdited = false;
settings = getSettings(call);
doShow(call);
}
@PluginMethod
public void pickImages(PluginCall call) {
settings = getSettings(call);
openPhotos(call, true);
}
@PluginMethod
public void pickLimitedLibraryPhotos(PluginCall call) {
call.unimplemented("not supported on android");
}
@PluginMethod
public void getLimitedLibraryPhotos(PluginCall call) {
call.unimplemented("not supported on android");
}
private void doShow(PluginCall call) {
switch (settings.getSource()) {
case CAMERA:
showCamera(call);
break;
case PHOTOS:
showPhotos(call);
break;
default:
showPrompt(call);
break;
}
}
private void showPrompt(final PluginCall call) {
// We have all necessary permissions, open the camera
List<String> options = new ArrayList<>();
options.add(call.getString("promptLabelPhoto", "From Photos"));
options.add(call.getString("promptLabelPicture", "Take Picture"));
final CameraBottomSheetDialogFragment fragment = new CameraBottomSheetDialogFragment();
fragment.setTitle(call.getString("promptLabelHeader", "Photo"));
fragment.setOptions(
options,
index -> {
if (index == 0) {
settings.setSource(CameraSource.PHOTOS);
openPhotos(call);
} else if (index == 1) {
settings.setSource(CameraSource.CAMERA);
openCamera(call);
}
},
() -> call.reject(USER_CANCELLED)
);
fragment.show(getActivity().getSupportFragmentManager(), "capacitorModalsActionSheet");
}
private void showCamera(final PluginCall call) {
if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
call.reject(NO_CAMERA_ERROR);
return;
}
openCamera(call);
}
private void showPhotos(final PluginCall call) {
openPhotos(call);
}
private boolean checkCameraPermissions(PluginCall call) {
// if the manifest does not contain the camera permissions key, we don't need to ask the user
boolean needCameraPerms = isPermissionDeclared(CAMERA);
boolean hasCameraPerms = !needCameraPerms || getPermissionState(CAMERA) == PermissionState.GRANTED;
boolean hasGalleryPerms = getPermissionState(SAVE_GALLERY) == PermissionState.GRANTED;
// If we want to save to the gallery, we need two permissions
// actually we only need permissions to save to gallery for Android <= 9 (API 28)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// we might still need to request permission for the camera
if (!hasCameraPerms) {
requestPermissionForAlias(CAMERA, call, "cameraPermissionsCallback");
return false;
}
return true;
}
// we need to request permissions to save to gallery for Android <= 9
if (settings.isSaveToGallery() && !(hasCameraPerms && hasGalleryPerms) && isFirstRequest) {
isFirstRequest = false;
String[] aliases;
if (needCameraPerms) {
aliases = new String[] { CAMERA, SAVE_GALLERY };
} else {
aliases = new String[] { SAVE_GALLERY };
}
requestPermissionForAliases(aliases, call, "cameraPermissionsCallback");
return false;
}
// If we don't need to save to the gallery, we can just ask for camera permissions
else if (!hasCameraPerms) {
requestPermissionForAlias(CAMERA, call, "cameraPermissionsCallback");
return false;
}
return true;
}
/**
* Completes the plugin call after a camera permission request
*
* @see #getPhoto(PluginCall)
* @param call the plugin call
*/
@PermissionCallback
private void cameraPermissionsCallback(PluginCall call) {
if (call.getMethodName().equals("pickImages")) {
openPhotos(call, true);
} else {
if (settings.getSource() == CameraSource.CAMERA && getPermissionState(CAMERA) != PermissionState.GRANTED) {
Logger.debug(getLogTag(), "User denied camera permission: " + getPermissionState(CAMERA).toString());
call.reject(PERMISSION_DENIED_ERROR_CAMERA);
return;
}
doShow(call);
}
}
@Override
protected void requestPermissionForAliases(@NonNull String[] aliases, @NonNull PluginCall call, @NonNull String callbackName) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
for (int i = 0; i < aliases.length; i++) {
if (aliases[i].equals(SAVE_GALLERY)) {
aliases[i] = READ_EXTERNAL_STORAGE;
}
}
}
super.requestPermissionForAliases(aliases, call, callbackName);
}
private CameraSettings getSettings(PluginCall call) {
CameraSettings settings = new CameraSettings();
settings.setResultType(getResultType(call.getString("resultType")));
settings.setSaveToGallery(call.getBoolean("saveToGallery", CameraSettings.DEFAULT_SAVE_IMAGE_TO_GALLERY));
settings.setAllowEditing(call.getBoolean("allowEditing", false));
settings.setQuality(call.getInt("quality", CameraSettings.DEFAULT_QUALITY));
settings.setWidth(call.getInt("width", 0));
settings.setHeight(call.getInt("height", 0));
settings.setShouldResize(settings.getWidth() > 0 || settings.getHeight() > 0);
settings.setShouldCorrectOrientation(call.getBoolean("correctOrientation", CameraSettings.DEFAULT_CORRECT_ORIENTATION));
try {
settings.setSource(CameraSource.valueOf(call.getString("source", CameraSource.PROMPT.getSource())));
} catch (IllegalArgumentException ex) {
settings.setSource(CameraSource.PROMPT);
}
return settings;
}
private CameraResultType getResultType(String resultType) {
if (resultType == null) {
return null;
}
try {
return CameraResultType.valueOf(resultType.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException ex) {
Logger.debug(getLogTag(), "Invalid result type \"" + resultType + "\", defaulting to base64");
return CameraResultType.BASE64;
}
}
public void openCamera(final PluginCall call) {
if (checkCameraPermissions(call)) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getContext().getPackageManager()) != null) {
// If we will be saving the photo, send the target file along
try {
String appId = getAppId();
File photoFile = CameraUtils.createImageFile(getActivity());
imageFileSavePath = photoFile.getAbsolutePath();
// TODO: Verify provider config exists
imageFileUri = FileProvider.getUriForFile(getActivity(), appId + ".fileprovider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
} catch (Exception ex) {
call.reject(IMAGE_FILE_SAVE_ERROR, ex);
return;
}
startActivityForResult(call, takePictureIntent, "processCameraImage");
} else {
call.reject(NO_CAMERA_ACTIVITY_ERROR);
}
}
}
public void openPhotos(final PluginCall call) {
openPhotos(call, false);
}
private <I, O> ActivityResultLauncher<I> registerActivityResultLauncher(
ActivityResultContract<I, O> contract,
ActivityResultCallback<O> callback
) {
String key = "cap_activity_rq#" + mNextLocalRequestCode.getAndIncrement();
if (bridge.getFragment() != null) {
Object host = bridge.getFragment().getHost();
if (host instanceof ActivityResultRegistryOwner) {
return ((ActivityResultRegistryOwner) host).getActivityResultRegistry().register(key, contract, callback);
}
return bridge.getFragment().requireActivity().getActivityResultRegistry().register(key, contract, callback);
}
return bridge.getActivity().getActivityResultRegistry().register(key, contract, callback);
}
private ActivityResultContract<PickVisualMediaRequest, List<Uri>> getContractForCall(final PluginCall call) {
int limit = call.getInt("limit", 0);
if (limit > 1) {
return new ActivityResultContracts.PickMultipleVisualMedia(limit);
} else {
return new ActivityResultContracts.PickMultipleVisualMedia();
}
}
private void openPhotos(final PluginCall call, boolean multiple) {
try {
if (multiple) {
pickMultipleMedia =
registerActivityResultLauncher(
getContractForCall(call),
uris -> {
if (!uris.isEmpty()) {
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(
() -> {
JSObject ret = new JSObject();
JSArray photos = new JSArray();
for (Uri imageUri : uris) {
try {
JSObject processResult = processPickedImages(imageUri);
if (
processResult.getString("error") != null && !processResult.getString("error").isEmpty()
) {
call.reject(processResult.getString("error"));
return;
} else {
photos.put(processResult);
}
} catch (SecurityException ex) {
call.reject("SecurityException");
}
}
ret.put("photos", photos);
call.resolve(ret);
}
);
} else {
call.reject(USER_CANCELLED);
}
pickMultipleMedia.unregister();
}
);
pickMultipleMedia.launch(
new PickVisualMediaRequest.Builder().setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE).build()
);
} else {
pickMedia =
registerActivityResultLauncher(
new ActivityResultContracts.PickVisualMedia(),
uri -> {
if (uri != null) {
imagePickedContentUri = uri;
processPickedImage(uri, call);
} else {
call.reject(USER_CANCELLED);
}
pickMedia.unregister();
}
);
pickMedia.launch(
new PickVisualMediaRequest.Builder().setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE).build()
);
}
} catch (ActivityNotFoundException ex) {
call.reject(NO_PHOTO_ACTIVITY_ERROR);
}
}
@ActivityCallback
public void processCameraImage(PluginCall call, ActivityResult result) {
settings = getSettings(call);
if (imageFileSavePath == null) {
call.reject(IMAGE_PROCESS_NO_FILE_ERROR);
return;
}
// Load the image as a Bitmap
File f = new File(imageFileSavePath);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
Uri contentUri = Uri.fromFile(f);
Bitmap bitmap = BitmapFactory.decodeFile(imageFileSavePath, bmOptions);
if (bitmap == null) {
call.reject(USER_CANCELLED);
return;
}
returnResult(call, bitmap, contentUri);
}
public void processPickedImage(PluginCall call, ActivityResult result) {
settings = getSettings(call);
Intent data = result.getData();
if (data == null) {
call.reject(USER_CANCELLED);
return;
}
Uri u = data.getData();
imagePickedContentUri = u;
processPickedImage(u, call);
}
@SuppressWarnings("deprecation")
private ArrayList<Parcelable> getLegacyParcelableArrayList(Bundle bundle, String key) {
return bundle.getParcelableArrayList(key);
}
private void processPickedImage(Uri imageUri, PluginCall call) {
InputStream imageStream = null;
try {
imageStream = getContext().getContentResolver().openInputStream(imageUri);
Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
if (bitmap == null) {
call.reject("Unable to process bitmap");
return;
}
returnResult(call, bitmap, imageUri);
} catch (OutOfMemoryError err) {
call.reject("Out of memory");
} catch (FileNotFoundException ex) {
call.reject("No such image found", ex);
} finally {
if (imageStream != null) {
try {
imageStream.close();
} catch (IOException e) {
Logger.error(getLogTag(), UNABLE_TO_PROCESS_IMAGE, e);
}
}
}
}
private JSObject processPickedImages(Uri imageUri) {
InputStream imageStream = null;
JSObject ret = new JSObject();
try {
imageStream = getContext().getContentResolver().openInputStream(imageUri);
Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
if (bitmap == null) {
ret.put("error", "Unable to process bitmap");
return ret;
}
ExifWrapper exif = ImageUtils.getExifData(getContext(), bitmap, imageUri);
try {
bitmap = prepareBitmap(bitmap, imageUri, exif);
} catch (IOException e) {
ret.put("error", UNABLE_TO_PROCESS_IMAGE);
return ret;
}
// Compress the final image and prepare for output to client
ByteArrayOutputStream bitmapOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, settings.getQuality(), bitmapOutputStream);
Uri newUri = getTempImage(imageUri, bitmapOutputStream);
exif.copyExif(newUri.getPath());
if (newUri != null) {
ret.put("format", "jpeg");
ret.put("exif", exif.toJson());
ret.put("path", newUri.toString());
ret.put("webPath", FileUtils.getPortablePath(getContext(), bridge.getLocalUrl(), newUri));
} else {
ret.put("error", UNABLE_TO_PROCESS_IMAGE);
}
return ret;
} catch (OutOfMemoryError err) {
ret.put("error", "Out of memory");
} catch (FileNotFoundException ex) {
ret.put("error", "No such image found");
Logger.error(getLogTag(), "No such image found", ex);
} finally {
if (imageStream != null) {
try {
imageStream.close();
} catch (IOException e) {
Logger.error(getLogTag(), UNABLE_TO_PROCESS_IMAGE, e);
}
}
}
return ret;
}
@ActivityCallback
private void processEditedImage(PluginCall call, ActivityResult result) {
isEdited = true;
settings = getSettings(call);
if (result.getResultCode() == Activity.RESULT_CANCELED) {
// User cancelled the edit operation, if this file was picked from photos,
// process the original picked image, otherwise process it as a camera photo
if (imagePickedContentUri != null) {
processPickedImage(imagePickedContentUri, call);
} else {
processCameraImage(call, result);
}
} else {
processPickedImage(call, result);
}
}
/**
* Save the modified image on the same path,
* or on a temporary location if it's a content url
* @param uri
* @param is
* @return
* @throws IOException
*/
private Uri saveImage(Uri uri, InputStream is) throws IOException {
File outFile = null;
if (uri.getScheme().equals("content")) {
outFile = getTempFile(uri);
} else {
outFile = new File(uri.getPath());
}
try {
writePhoto(outFile, is);
} catch (FileNotFoundException ex) {
// Some gallery apps return read only file url, create a temporary file for modifications
outFile = getTempFile(uri);
writePhoto(outFile, is);
}
return Uri.fromFile(outFile);
}
private void writePhoto(File outFile, InputStream is) throws IOException {
FileOutputStream fos = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
}
private File getTempFile(Uri uri) {
String filename = Uri.parse(Uri.decode(uri.toString())).getLastPathSegment();
if (!filename.contains(".jpg") && !filename.contains(".jpeg")) {
filename += "." + (new java.util.Date()).getTime() + ".jpeg";
}
File cacheDir = getContext().getCacheDir();
return new File(cacheDir, filename);
}
/**
* After processing the image, return the final result back to the caller.
* @param call
* @param bitmap
* @param u
*/
@SuppressWarnings("deprecation")
private void returnResult(PluginCall call, Bitmap bitmap, Uri u) {
ExifWrapper exif = ImageUtils.getExifData(getContext(), bitmap, u);
try {
bitmap = prepareBitmap(bitmap, u, exif);
} catch (IOException e) {
call.reject(UNABLE_TO_PROCESS_IMAGE);
return;
}
// Compress the final image and prepare for output to client
ByteArrayOutputStream bitmapOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, settings.getQuality(), bitmapOutputStream);
if (settings.isAllowEditing() && !isEdited) {
editImage(call, u, bitmapOutputStream);
return;
}
boolean saveToGallery = call.getBoolean("saveToGallery", CameraSettings.DEFAULT_SAVE_IMAGE_TO_GALLERY);
if (saveToGallery && (imageEditedFileSavePath != null || imageFileSavePath != null)) {
isSaved = true;
try {
String fileToSavePath = imageEditedFileSavePath != null ? imageEditedFileSavePath : imageFileSavePath;
File fileToSave = new File(fileToSavePath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileToSave.getName());
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Uri uri = resolver.insert(contentUri, values);
if (uri == null) {
throw new IOException("Failed to create new MediaStore record.");
}
OutputStream stream = resolver.openOutputStream(uri);
if (stream == null) {
throw new IOException("Failed to open output stream.");
}
Boolean inserted = bitmap.compress(Bitmap.CompressFormat.JPEG, settings.getQuality(), stream);
if (!inserted) {
isSaved = false;
}
} else {
String inserted = MediaStore.Images.Media.insertImage(
getContext().getContentResolver(),
fileToSavePath,
fileToSave.getName(),
""
);
if (inserted == null) {
isSaved = false;
}
}
} catch (FileNotFoundException e) {
isSaved = false;
Logger.error(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e);
} catch (IOException e) {
isSaved = false;
Logger.error(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e);
}
}
if (settings.getResultType() == CameraResultType.BASE64) {
returnBase64(call, exif, bitmapOutputStream);
} else if (settings.getResultType() == CameraResultType.URI) {
returnFileURI(call, exif, bitmap, u, bitmapOutputStream);
} else if (settings.getResultType() == CameraResultType.DATAURL) {
returnDataUrl(call, exif, bitmapOutputStream);
} else {
call.reject(INVALID_RESULT_TYPE_ERROR);
}
// Result returned, clear stored paths and images
if (settings.getResultType() != CameraResultType.URI) {
deleteImageFile();
}
imageFileSavePath = null;
imageFileUri = null;
imagePickedContentUri = null;
imageEditedFileSavePath = null;
}
private void deleteImageFile() {
if (imageFileSavePath != null && !settings.isSaveToGallery()) {
File photoFile = new File(imageFileSavePath);
if (photoFile.exists()) {
photoFile.delete();
}
}
}
private void returnFileURI(PluginCall call, ExifWrapper exif, Bitmap bitmap, Uri u, ByteArrayOutputStream bitmapOutputStream) {
Uri newUri = getTempImage(u, bitmapOutputStream);
exif.copyExif(newUri.getPath());
if (newUri != null) {
JSObject ret = new JSObject();
ret.put("format", "jpeg");
ret.put("exif", exif.toJson());
ret.put("path", newUri.toString());
ret.put("webPath", FileUtils.getPortablePath(getContext(), bridge.getLocalUrl(), newUri));
ret.put("saved", isSaved);
call.resolve(ret);
} else {
call.reject(UNABLE_TO_PROCESS_IMAGE);
}
}
private Uri getTempImage(Uri u, ByteArrayOutputStream bitmapOutputStream) {
ByteArrayInputStream bis = null;
Uri newUri = null;
try {
bis = new ByteArrayInputStream(bitmapOutputStream.toByteArray());
newUri = saveImage(u, bis);
} catch (IOException ex) {} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
Logger.error(getLogTag(), UNABLE_TO_PROCESS_IMAGE, e);
}
}
}
return newUri;
}
/**
* Apply our standard processing of the bitmap, returning a new one and
* recycling the old one in the process
* @param bitmap
* @param imageUri
* @param exif
* @return
*/
private Bitmap prepareBitmap(Bitmap bitmap, Uri imageUri, ExifWrapper exif) throws IOException {
if (settings.isShouldCorrectOrientation()) {
final Bitmap newBitmap = ImageUtils.correctOrientation(getContext(), bitmap, imageUri, exif);
bitmap = replaceBitmap(bitmap, newBitmap);
}
if (settings.isShouldResize()) {
final Bitmap newBitmap = ImageUtils.resize(bitmap, settings.getWidth(), settings.getHeight());
bitmap = replaceBitmap(bitmap, newBitmap);
}
return bitmap;
}
private Bitmap replaceBitmap(Bitmap bitmap, final Bitmap newBitmap) {
if (bitmap != newBitmap) {
bitmap.recycle();
}
bitmap = newBitmap;
return bitmap;
}
private void returnDataUrl(PluginCall call, ExifWrapper exif, ByteArrayOutputStream bitmapOutputStream) {
byte[] byteArray = bitmapOutputStream.toByteArray();
String encoded = Base64.encodeToString(byteArray, Base64.NO_WRAP);
JSObject data = new JSObject();
data.put("format", "jpeg");
data.put("dataUrl", "data:image/jpeg;base64," + encoded);
data.put("exif", exif.toJson());
call.resolve(data);
}
private void returnBase64(PluginCall call, ExifWrapper exif, ByteArrayOutputStream bitmapOutputStream) {
byte[] byteArray = bitmapOutputStream.toByteArray();
String encoded = Base64.encodeToString(byteArray, Base64.NO_WRAP);
JSObject data = new JSObject();
data.put("format", "jpeg");
data.put("base64String", encoded);
data.put("exif", exif.toJson());
call.resolve(data);
}
@Override
@PluginMethod
public void requestPermissions(PluginCall call) {
// If the camera permission is defined in the manifest, then we have to prompt the user
// or else we will get a security exception when trying to present the camera. If, however,
// it is not defined in the manifest then we don't need to prompt and it will just work.
if (isPermissionDeclared(CAMERA)) {
// just request normally
super.requestPermissions(call);
} else {
// the manifest does not define camera permissions, so we need to decide what to do
// first, extract the permissions being requested
JSArray providedPerms = call.getArray("permissions");
List<String> permsList = null;
if (providedPerms != null) {
try {
permsList = providedPerms.toList();
} catch (JSONException e) {}
}
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ||
(permsList != null && permsList.size() == 1 && (permsList.contains(CAMERA) || permsList.contains(PHOTOS)))
) {
// either we're on Android 13+ (storage permissions do not apply)
// or the only thing being asked for was the camera so we can just return the current state
checkPermissions(call);
} else {
requestPermissionForAlias(SAVE_GALLERY, call, "checkPermissions");
}
}
}
@Override
public Map<String, PermissionState> getPermissionStates() {
Map<String, PermissionState> permissionStates = super.getPermissionStates();
// If Camera is not in the manifest and therefore not required, say the permission is granted
if (!isPermissionDeclared(CAMERA)) {
permissionStates.put(CAMERA, PermissionState.GRANTED);
}
if (permissionStates.containsKey(PHOTOS)) {
permissionStates.put(PHOTOS, PermissionState.GRANTED);
}
// If the SDK version is 30 or higher, update the SAVE_GALLERY state to match the READ_EXTERNAL_STORAGE state.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
String alias = READ_EXTERNAL_STORAGE;
if (permissionStates.containsKey(alias)) {
permissionStates.put(SAVE_GALLERY, permissionStates.get(alias));
}
}
return permissionStates;
}
private void editImage(PluginCall call, Uri uri, ByteArrayOutputStream bitmapOutputStream) {
try {
Uri tempImage = getTempImage(uri, bitmapOutputStream);
Intent editIntent = createEditIntent(tempImage);
if (editIntent != null) {
startActivityForResult(call, editIntent, "processEditedImage");
} else {
call.reject(IMAGE_EDIT_ERROR);
}
} catch (Exception ex) {
call.reject(IMAGE_EDIT_ERROR, ex);
}
}
private Intent createEditIntent(Uri origPhotoUri) {
try {
File editFile = new File(origPhotoUri.getPath());
Uri editUri = FileProvider.getUriForFile(getActivity(), getContext().getPackageName() + ".fileprovider", editFile);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
editIntent.setDataAndType(editUri, "image/*");
imageEditedFileSavePath = editFile.getAbsolutePath();
int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
editIntent.addFlags(flags);
editIntent.putExtra(MediaStore.EXTRA_OUTPUT, editUri);
List<ResolveInfo> resInfoList;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
resInfoList =
getContext()
.getPackageManager()
.queryIntentActivities(editIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
} else {
resInfoList = legacyQueryIntentActivities(editIntent);
}
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
getContext().grantUriPermission(packageName, editUri, flags);
}
return editIntent;
} catch (Exception ex) {
return null;
}
}
@SuppressWarnings("deprecation")
private List<ResolveInfo> legacyQueryIntentActivities(Intent intent) {
return getContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
}
@Override
protected Bundle saveInstanceState() {
Bundle bundle = super.saveInstanceState();
if (bundle != null) {
bundle.putString("cameraImageFileSavePath", imageFileSavePath);
}
return bundle;
}
@Override
protected void restoreState(Bundle state) {
String storedImageFileSavePath = state.getString("cameraImageFileSavePath");
if (storedImageFileSavePath != null) {
imageFileSavePath = storedImageFileSavePath;
}
}
/**
* Unregister activity result launches to prevent leaks.
*/
@Override
protected void handleOnDestroy() {
if (pickMedia != null) {
pickMedia.unregister();
}
if (pickMultipleMedia != null) {
pickMultipleMedia.unregister();
}
}
}

View File

@@ -0,0 +1,17 @@
package com.capacitorjs.plugins.camera;
public enum CameraResultType {
BASE64("base64"),
URI("uri"),
DATAURL("dataUrl");
private String type;
CameraResultType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}

View File

@@ -0,0 +1,90 @@
package com.capacitorjs.plugins.camera;
public class CameraSettings {
public static final int DEFAULT_QUALITY = 90;
public static final boolean DEFAULT_SAVE_IMAGE_TO_GALLERY = false;
public static final boolean DEFAULT_CORRECT_ORIENTATION = true;
private CameraResultType resultType = CameraResultType.BASE64;
private int quality = DEFAULT_QUALITY;
private boolean shouldResize = false;
private boolean shouldCorrectOrientation = DEFAULT_CORRECT_ORIENTATION;
private boolean saveToGallery = DEFAULT_SAVE_IMAGE_TO_GALLERY;
private boolean allowEditing = false;
private int width = 0;
private int height = 0;
private CameraSource source = CameraSource.PROMPT;
public CameraResultType getResultType() {
return resultType;
}
public void setResultType(CameraResultType resultType) {
this.resultType = resultType;
}
public int getQuality() {
return quality;
}
public void setQuality(int quality) {
this.quality = quality;
}
public boolean isShouldResize() {
return shouldResize;
}
public void setShouldResize(boolean shouldResize) {
this.shouldResize = shouldResize;
}
public boolean isShouldCorrectOrientation() {
return shouldCorrectOrientation;
}
public void setShouldCorrectOrientation(boolean shouldCorrectOrientation) {
this.shouldCorrectOrientation = shouldCorrectOrientation;
}
public boolean isSaveToGallery() {
return saveToGallery;
}
public void setSaveToGallery(boolean saveToGallery) {
this.saveToGallery = saveToGallery;
}
public boolean isAllowEditing() {
return allowEditing;
}
public void setAllowEditing(boolean allowEditing) {
this.allowEditing = allowEditing;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public CameraSource getSource() {
return source;
}
public void setSource(CameraSource source) {
this.source = source;
}
}

View File

@@ -0,0 +1,17 @@
package com.capacitorjs.plugins.camera;
public enum CameraSource {
PROMPT("PROMPT"),
CAMERA("CAMERA"),
PHOTOS("PHOTOS");
private String source;
CameraSource(String source) {
this.source = source;
}
public String getSource() {
return this.source;
}
}

View File

@@ -0,0 +1,34 @@
package com.capacitorjs.plugins.camera;
import android.app.Activity;
import android.net.Uri;
import android.os.Environment;
import androidx.core.content.FileProvider;
import com.getcapacitor.Logger;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CameraUtils {
public static Uri createImageFileUri(Activity activity, String appId) throws IOException {
File photoFile = CameraUtils.createImageFile(activity);
return FileProvider.getUriForFile(activity, appId + ".fileprovider", photoFile);
}
public static File createImageFile(Activity activity) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(imageFileName, /* prefix */".jpg", /* suffix */storageDir/* directory */);
return image;
}
protected static String getLogTag() {
return Logger.tags("CameraUtils");
}
}

View File

@@ -0,0 +1,205 @@
package com.capacitorjs.plugins.camera;
import static androidx.exifinterface.media.ExifInterface.*;
import androidx.exifinterface.media.ExifInterface;
import com.getcapacitor.JSObject;
public class ExifWrapper {
private final ExifInterface exif;
private final String[] attributes = new String[] {
TAG_APERTURE_VALUE,
TAG_ARTIST,
TAG_BITS_PER_SAMPLE,
TAG_BODY_SERIAL_NUMBER,
TAG_BRIGHTNESS_VALUE,
TAG_CAMERA_OWNER_NAME,
TAG_CFA_PATTERN,
TAG_COLOR_SPACE,
TAG_COMPONENTS_CONFIGURATION,
TAG_COMPRESSED_BITS_PER_PIXEL,
TAG_COMPRESSION,
TAG_CONTRAST,
TAG_COPYRIGHT,
TAG_CUSTOM_RENDERED,
TAG_DATETIME,
TAG_DATETIME_DIGITIZED,
TAG_DATETIME_ORIGINAL,
TAG_DEFAULT_CROP_SIZE,
TAG_DEVICE_SETTING_DESCRIPTION,
TAG_DIGITAL_ZOOM_RATIO,
TAG_DNG_VERSION,
TAG_EXIF_VERSION,
TAG_EXPOSURE_BIAS_VALUE,
TAG_EXPOSURE_INDEX,
TAG_EXPOSURE_MODE,
TAG_EXPOSURE_PROGRAM,
TAG_EXPOSURE_TIME,
TAG_FILE_SOURCE,
TAG_FLASH,
TAG_FLASHPIX_VERSION,
TAG_FLASH_ENERGY,
TAG_FOCAL_LENGTH,
TAG_FOCAL_LENGTH_IN_35MM_FILM,
TAG_FOCAL_PLANE_RESOLUTION_UNIT,
TAG_FOCAL_PLANE_X_RESOLUTION,
TAG_FOCAL_PLANE_Y_RESOLUTION,
TAG_F_NUMBER,
TAG_GAIN_CONTROL,
TAG_GAMMA,
TAG_GPS_ALTITUDE,
TAG_GPS_ALTITUDE_REF,
TAG_GPS_AREA_INFORMATION,
TAG_GPS_DATESTAMP,
TAG_GPS_DEST_BEARING,
TAG_GPS_DEST_BEARING_REF,
TAG_GPS_DEST_DISTANCE,
TAG_GPS_DEST_DISTANCE_REF,
TAG_GPS_DEST_LATITUDE,
TAG_GPS_DEST_LATITUDE_REF,
TAG_GPS_DEST_LONGITUDE,
TAG_GPS_DEST_LONGITUDE_REF,
TAG_GPS_DIFFERENTIAL,
TAG_GPS_DOP,
TAG_GPS_H_POSITIONING_ERROR,
TAG_GPS_IMG_DIRECTION,
TAG_GPS_IMG_DIRECTION_REF,
TAG_GPS_LATITUDE,
TAG_GPS_LATITUDE_REF,
TAG_GPS_LONGITUDE,
TAG_GPS_LONGITUDE_REF,
TAG_GPS_MAP_DATUM,
TAG_GPS_MEASURE_MODE,
TAG_GPS_PROCESSING_METHOD,
TAG_GPS_SATELLITES,
TAG_GPS_SPEED,
TAG_GPS_SPEED_REF,
TAG_GPS_STATUS,
TAG_GPS_TIMESTAMP,
TAG_GPS_TRACK,
TAG_GPS_TRACK_REF,
TAG_GPS_VERSION_ID,
TAG_IMAGE_DESCRIPTION,
TAG_IMAGE_LENGTH,
TAG_IMAGE_UNIQUE_ID,
TAG_IMAGE_WIDTH,
TAG_INTEROPERABILITY_INDEX,
TAG_ISO_SPEED,
TAG_ISO_SPEED_LATITUDE_YYY,
TAG_ISO_SPEED_LATITUDE_ZZZ,
TAG_JPEG_INTERCHANGE_FORMAT,
TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
TAG_LENS_MAKE,
TAG_LENS_MODEL,
TAG_LENS_SERIAL_NUMBER,
TAG_LENS_SPECIFICATION,
TAG_LIGHT_SOURCE,
TAG_MAKE,
TAG_MAKER_NOTE,
TAG_MAX_APERTURE_VALUE,
TAG_METERING_MODE,
TAG_MODEL,
TAG_NEW_SUBFILE_TYPE,
TAG_OECF,
TAG_OFFSET_TIME,
TAG_OFFSET_TIME_DIGITIZED,
TAG_OFFSET_TIME_ORIGINAL,
TAG_ORF_ASPECT_FRAME,
TAG_ORF_PREVIEW_IMAGE_LENGTH,
TAG_ORF_PREVIEW_IMAGE_START,
TAG_ORF_THUMBNAIL_IMAGE,
TAG_ORIENTATION,
TAG_PHOTOGRAPHIC_SENSITIVITY,
TAG_PHOTOMETRIC_INTERPRETATION,
TAG_PIXEL_X_DIMENSION,
TAG_PIXEL_Y_DIMENSION,
TAG_PLANAR_CONFIGURATION,
TAG_PRIMARY_CHROMATICITIES,
TAG_RECOMMENDED_EXPOSURE_INDEX,
TAG_REFERENCE_BLACK_WHITE,
TAG_RELATED_SOUND_FILE,
TAG_RESOLUTION_UNIT,
TAG_ROWS_PER_STRIP,
TAG_RW2_ISO,
TAG_RW2_JPG_FROM_RAW,
TAG_RW2_SENSOR_BOTTOM_BORDER,
TAG_RW2_SENSOR_LEFT_BORDER,
TAG_RW2_SENSOR_RIGHT_BORDER,
TAG_RW2_SENSOR_TOP_BORDER,
TAG_SAMPLES_PER_PIXEL,
TAG_SATURATION,
TAG_SCENE_CAPTURE_TYPE,
TAG_SCENE_TYPE,
TAG_SENSING_METHOD,
TAG_SENSITIVITY_TYPE,
TAG_SHARPNESS,
TAG_SHUTTER_SPEED_VALUE,
TAG_SOFTWARE,
TAG_SPATIAL_FREQUENCY_RESPONSE,
TAG_SPECTRAL_SENSITIVITY,
TAG_STANDARD_OUTPUT_SENSITIVITY,
TAG_STRIP_BYTE_COUNTS,
TAG_STRIP_OFFSETS,
TAG_SUBFILE_TYPE,
TAG_SUBJECT_AREA,
TAG_SUBJECT_DISTANCE,
TAG_SUBJECT_DISTANCE_RANGE,
TAG_SUBJECT_LOCATION,
TAG_SUBSEC_TIME,
TAG_SUBSEC_TIME_DIGITIZED,
TAG_SUBSEC_TIME_ORIGINAL,
TAG_THUMBNAIL_IMAGE_LENGTH,
TAG_THUMBNAIL_IMAGE_WIDTH,
TAG_TRANSFER_FUNCTION,
TAG_USER_COMMENT,
TAG_WHITE_BALANCE,
TAG_WHITE_POINT,
TAG_XMP,
TAG_X_RESOLUTION,
TAG_Y_CB_CR_COEFFICIENTS,
TAG_Y_CB_CR_POSITIONING,
TAG_Y_CB_CR_SUB_SAMPLING,
TAG_Y_RESOLUTION
};
public ExifWrapper(ExifInterface exif) {
this.exif = exif;
}
public JSObject toJson() {
JSObject ret = new JSObject();
if (this.exif == null) {
return ret;
}
for (int i = 0; i < attributes.length; i++) {
p(ret, attributes[i]);
}
return ret;
}
public void p(JSObject o, String tag) {
String val = exif.getAttribute(tag);
o.put(tag, val);
}
public void copyExif(String destFile) {
try {
ExifInterface destExif = new ExifInterface(destFile);
for (int i = 0; i < attributes.length; i++) {
String value = exif.getAttribute(attributes[i]);
if (value != null) {
destExif.setAttribute(attributes[i], value);
}
}
destExif.saveAttributes();
} catch (Exception ex) {}
}
public void resetOrientation() {
exif.resetOrientation();
}
}

View File

@@ -0,0 +1,124 @@
package com.capacitorjs.plugins.camera;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import androidx.exifinterface.media.ExifInterface;
import com.getcapacitor.Logger;
import java.io.IOException;
import java.io.InputStream;
public class ImageUtils {
/**
* Resize an image to the given max width and max height. Constraint can be put
* on one dimension, or both. Resize will always preserve aspect ratio.
* @param bitmap
* @param desiredMaxWidth
* @param desiredMaxHeight
* @return a new, scaled Bitmap
*/
public static Bitmap resize(Bitmap bitmap, final int desiredMaxWidth, final int desiredMaxHeight) {
return ImageUtils.resizePreservingAspectRatio(bitmap, desiredMaxWidth, desiredMaxHeight);
}
/**
* Resize an image to the given max width and max height. Constraint can be put
* on one dimension, or both. Resize will always preserve aspect ratio.
* @param bitmap
* @param desiredMaxWidth
* @param desiredMaxHeight
* @return a new, scaled Bitmap
*/
private static Bitmap resizePreservingAspectRatio(Bitmap bitmap, final int desiredMaxWidth, final int desiredMaxHeight) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// 0 is treated as 'no restriction'
int maxHeight = desiredMaxHeight == 0 ? height : desiredMaxHeight;
int maxWidth = desiredMaxWidth == 0 ? width : desiredMaxWidth;
// resize with preserved aspect ratio
float newWidth = Math.min(width, maxWidth);
float newHeight = (height * newWidth) / width;
if (newHeight > maxHeight) {
newWidth = (width * maxHeight) / height;
newHeight = maxHeight;
}
return Bitmap.createScaledBitmap(bitmap, Math.round(newWidth), Math.round(newHeight), false);
}
/**
* Transform an image with the given matrix
* @param bitmap
* @param matrix
* @return
*/
private static Bitmap transform(final Bitmap bitmap, final Matrix matrix) {
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
/**
* Correct the orientation of an image by reading its exif information and rotating
* the appropriate amount for portrait mode
* @param bitmap
* @param imageUri
* @param exif
* @return
*/
public static Bitmap correctOrientation(final Context c, final Bitmap bitmap, final Uri imageUri, ExifWrapper exif) throws IOException {
final int orientation = getOrientation(c, imageUri);
if (orientation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
exif.resetOrientation();
return transform(bitmap, matrix);
} else {
return bitmap;
}
}
private static int getOrientation(final Context c, final Uri imageUri) throws IOException {
int result = 0;
try (InputStream iStream = c.getContentResolver().openInputStream(imageUri)) {
final ExifInterface exifInterface = new ExifInterface(iStream);
final int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
result = 90;
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
result = 180;
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
result = 270;
}
}
return result;
}
public static ExifWrapper getExifData(final Context c, final Bitmap bitmap, final Uri imageUri) {
InputStream stream = null;
try {
stream = c.getContentResolver().openInputStream(imageUri);
final ExifInterface exifInterface = new ExifInterface(stream);
return new ExifWrapper(exifInterface);
} catch (IOException ex) {
Logger.error("Error loading exif data from image", ex);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException ignored) {}
}
}
return new ExifWrapper(null);
}
}

804
node_modules/@capacitor/camera/dist/docs.json generated vendored Normal file
View File

@@ -0,0 +1,804 @@
{
"api": {
"name": "CameraPlugin",
"slug": "cameraplugin",
"docs": "",
"tags": [],
"methods": [
{
"name": "getPhoto",
"signature": "(options: ImageOptions) => Promise<Photo>",
"parameters": [
{
"name": "options",
"docs": "",
"type": "ImageOptions"
}
],
"returns": "Promise<Photo>",
"tags": [
{
"name": "since",
"text": "1.0.0"
}
],
"docs": "Prompt the user to pick a photo from an album, or take a new photo\nwith the camera.",
"complexTypes": [
"Photo",
"ImageOptions"
],
"slug": "getphoto"
},
{
"name": "pickImages",
"signature": "(options: GalleryImageOptions) => Promise<GalleryPhotos>",
"parameters": [
{
"name": "options",
"docs": "",
"type": "GalleryImageOptions"
}
],
"returns": "Promise<GalleryPhotos>",
"tags": [
{
"name": "since",
"text": "1.2.0"
}
],
"docs": "Allows the user to pick multiple pictures from the photo gallery.\nOn iOS 13 and older it only allows to pick one picture.",
"complexTypes": [
"GalleryPhotos",
"GalleryImageOptions"
],
"slug": "pickimages"
},
{
"name": "pickLimitedLibraryPhotos",
"signature": "() => Promise<GalleryPhotos>",
"parameters": [],
"returns": "Promise<GalleryPhotos>",
"tags": [
{
"name": "since",
"text": "4.1.0"
}
],
"docs": "iOS 14+ Only: Allows the user to update their limited photo library selection.\nOn iOS 15+ returns all the limited photos after the picker dismissal.\nOn iOS 14 or if the user gave full access to the photos it returns an empty array.",
"complexTypes": [
"GalleryPhotos"
],
"slug": "picklimitedlibraryphotos"
},
{
"name": "getLimitedLibraryPhotos",
"signature": "() => Promise<GalleryPhotos>",
"parameters": [],
"returns": "Promise<GalleryPhotos>",
"tags": [
{
"name": "since",
"text": "4.1.0"
}
],
"docs": "iOS 14+ Only: Return an array of photos selected from the limited photo library.",
"complexTypes": [
"GalleryPhotos"
],
"slug": "getlimitedlibraryphotos"
},
{
"name": "checkPermissions",
"signature": "() => Promise<PermissionStatus>",
"parameters": [],
"returns": "Promise<PermissionStatus>",
"tags": [
{
"name": "since",
"text": "1.0.0"
}
],
"docs": "Check camera and photo album permissions",
"complexTypes": [
"PermissionStatus"
],
"slug": "checkpermissions"
},
{
"name": "requestPermissions",
"signature": "(permissions?: CameraPluginPermissions | undefined) => Promise<PermissionStatus>",
"parameters": [
{
"name": "permissions",
"docs": "",
"type": "CameraPluginPermissions | undefined"
}
],
"returns": "Promise<PermissionStatus>",
"tags": [
{
"name": "since",
"text": "1.0.0"
}
],
"docs": "Request camera and photo album permissions",
"complexTypes": [
"PermissionStatus",
"CameraPluginPermissions"
],
"slug": "requestpermissions"
}
],
"properties": []
},
"interfaces": [
{
"name": "Photo",
"slug": "photo",
"docs": "",
"tags": [],
"methods": [],
"properties": [
{
"name": "base64String",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "The base64 encoded string representation of the image, if using CameraResultType.Base64.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "dataUrl",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "The url starting with 'data:image/jpeg;base64,' and the base64 encoded string representation of the image, if using CameraResultType.DataUrl.\n\nNote: On web, the file format could change depending on the browser.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "path",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "If using CameraResultType.Uri, the path will contain a full,\nplatform-specific file URL that can be read later using the Filesystem API.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "webPath",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "webPath returns a path that can be used to set the src attribute of an image for efficient\nloading and rendering.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "exif",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Exif data, if any, retrieved from the image",
"complexTypes": [],
"type": "any"
},
{
"name": "format",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "The format of the image, ex: jpeg, png, gif.\n\niOS and Android only support jpeg.\nWeb supports jpeg, png and gif, but the exact availability may vary depending on the browser.\ngif is only supported if `webUseInput` is set to `true` or if `source` is set to `Photos`.",
"complexTypes": [],
"type": "string"
},
{
"name": "saved",
"tags": [
{
"text": "1.1.0",
"name": "since"
}
],
"docs": "Whether if the image was saved to the gallery or not.\n\nOn Android and iOS, saving to the gallery can fail if the user didn't\ngrant the required permissions.\nOn Web there is no gallery, so always returns false.",
"complexTypes": [],
"type": "boolean"
}
]
},
{
"name": "ImageOptions",
"slug": "imageoptions",
"docs": "",
"tags": [],
"methods": [],
"properties": [
{
"name": "quality",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "The quality of image to return as JPEG, from 0-100\nNote: This option is only supported on Android and iOS",
"complexTypes": [],
"type": "number | undefined"
},
{
"name": "allowEditing",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Whether to allow the user to crop or make small edits (platform specific).\nOn iOS 14+ it's only supported for CameraSource.Camera, but not for CameraSource.Photos.",
"complexTypes": [],
"type": "boolean | undefined"
},
{
"name": "resultType",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "How the data should be returned. Currently, only 'Base64', 'DataUrl' or 'Uri' is supported",
"complexTypes": [
"CameraResultType"
],
"type": "CameraResultType"
},
{
"name": "saveToGallery",
"tags": [
{
"text": ": false",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Whether to save the photo to the gallery.\nIf the photo was picked from the gallery, it will only be saved if edited.",
"complexTypes": [],
"type": "boolean | undefined"
},
{
"name": "width",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "The desired maximum width of the saved image. The aspect ratio is respected.",
"complexTypes": [],
"type": "number | undefined"
},
{
"name": "height",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "The desired maximum height of the saved image. The aspect ratio is respected.",
"complexTypes": [],
"type": "number | undefined"
},
{
"name": "correctOrientation",
"tags": [
{
"text": ": true",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Whether to automatically rotate the image \"up\" to correct for orientation\nin portrait mode",
"complexTypes": [],
"type": "boolean | undefined"
},
{
"name": "source",
"tags": [
{
"text": ": CameraSource.Prompt",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "The source to get the photo from. By default this prompts the user to select\neither the photo album or take a photo.",
"complexTypes": [
"CameraSource"
],
"type": "CameraSource"
},
{
"name": "direction",
"tags": [
{
"text": ": CameraDirection.Rear",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "iOS and Web only: The camera direction.",
"complexTypes": [
"CameraDirection"
],
"type": "CameraDirection"
},
{
"name": "presentationStyle",
"tags": [
{
"text": ": 'fullscreen'",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "iOS only: The presentation style of the Camera.",
"complexTypes": [],
"type": "'fullscreen' | 'popover' | undefined"
},
{
"name": "webUseInput",
"tags": [
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Web only: Whether to use the PWA Element experience or file input. The\ndefault is to use PWA Elements if installed and fall back to file input.\nTo always use file input, set this to `true`.\n\nLearn more about PWA Elements: https://capacitorjs.com/docs/web/pwa-elements",
"complexTypes": [],
"type": "boolean | undefined"
},
{
"name": "promptLabelHeader",
"tags": [
{
"text": ": 'Photo'",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Text value to use when displaying the prompt.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "promptLabelCancel",
"tags": [
{
"text": ": 'Cancel'",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Text value to use when displaying the prompt.\niOS only: The label of the 'cancel' button.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "promptLabelPhoto",
"tags": [
{
"text": ": 'From Photos'",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Text value to use when displaying the prompt.\nThe label of the button to select a saved image.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "promptLabelPicture",
"tags": [
{
"text": ": 'Take Picture'",
"name": "default"
},
{
"text": "1.0.0",
"name": "since"
}
],
"docs": "Text value to use when displaying the prompt.\nThe label of the button to open the camera.",
"complexTypes": [],
"type": "string | undefined"
}
]
},
{
"name": "GalleryPhotos",
"slug": "galleryphotos",
"docs": "",
"tags": [],
"methods": [],
"properties": [
{
"name": "photos",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "Array of all the picked photos.",
"complexTypes": [
"GalleryPhoto"
],
"type": "GalleryPhoto[]"
}
]
},
{
"name": "GalleryPhoto",
"slug": "galleryphoto",
"docs": "",
"tags": [],
"methods": [],
"properties": [
{
"name": "path",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "Full, platform-specific file URL that can be read later using the Filesystem API.",
"complexTypes": [],
"type": "string | undefined"
},
{
"name": "webPath",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "webPath returns a path that can be used to set the src attribute of an image for efficient\nloading and rendering.",
"complexTypes": [],
"type": "string"
},
{
"name": "exif",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "Exif data, if any, retrieved from the image",
"complexTypes": [],
"type": "any"
},
{
"name": "format",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "The format of the image, ex: jpeg, png, gif.\n\niOS and Android only support jpeg.\nWeb supports jpeg, png and gif.",
"complexTypes": [],
"type": "string"
}
]
},
{
"name": "GalleryImageOptions",
"slug": "galleryimageoptions",
"docs": "",
"tags": [],
"methods": [],
"properties": [
{
"name": "quality",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "The quality of image to return as JPEG, from 0-100\nNote: This option is only supported on Android and iOS.",
"complexTypes": [],
"type": "number | undefined"
},
{
"name": "width",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "The desired maximum width of the saved image. The aspect ratio is respected.",
"complexTypes": [],
"type": "number | undefined"
},
{
"name": "height",
"tags": [
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "The desired maximum height of the saved image. The aspect ratio is respected.",
"complexTypes": [],
"type": "number | undefined"
},
{
"name": "correctOrientation",
"tags": [
{
"text": ": true",
"name": "default"
},
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "Whether to automatically rotate the image \"up\" to correct for orientation\nin portrait mode",
"complexTypes": [],
"type": "boolean | undefined"
},
{
"name": "presentationStyle",
"tags": [
{
"text": ": 'fullscreen'",
"name": "default"
},
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "iOS only: The presentation style of the Camera.",
"complexTypes": [],
"type": "'fullscreen' | 'popover' | undefined"
},
{
"name": "limit",
"tags": [
{
"text": "0 (unlimited)",
"name": "default"
},
{
"text": "1.2.0",
"name": "since"
}
],
"docs": "Maximum number of pictures the user will be able to choose.\nNote: This option is only supported on Android 13+ and iOS.",
"complexTypes": [],
"type": "number | undefined"
}
]
},
{
"name": "PermissionStatus",
"slug": "permissionstatus",
"docs": "",
"tags": [],
"methods": [],
"properties": [
{
"name": "camera",
"tags": [],
"docs": "",
"complexTypes": [
"CameraPermissionState"
],
"type": "CameraPermissionState"
},
{
"name": "photos",
"tags": [],
"docs": "",
"complexTypes": [
"CameraPermissionState"
],
"type": "CameraPermissionState"
}
]
},
{
"name": "CameraPluginPermissions",
"slug": "camerapluginpermissions",
"docs": "",
"tags": [],
"methods": [],
"properties": [
{
"name": "permissions",
"tags": [],
"docs": "",
"complexTypes": [
"CameraPermissionType"
],
"type": "CameraPermissionType[]"
}
]
}
],
"enums": [
{
"name": "CameraResultType",
"slug": "cameraresulttype",
"members": [
{
"name": "Uri",
"value": "'uri'",
"tags": [],
"docs": ""
},
{
"name": "Base64",
"value": "'base64'",
"tags": [],
"docs": ""
},
{
"name": "DataUrl",
"value": "'dataUrl'",
"tags": [],
"docs": ""
}
]
},
{
"name": "CameraSource",
"slug": "camerasource",
"members": [
{
"name": "Prompt",
"value": "'PROMPT'",
"tags": [],
"docs": "Prompts the user to select either the photo album or take a photo."
},
{
"name": "Camera",
"value": "'CAMERA'",
"tags": [],
"docs": "Take a new photo using the camera."
},
{
"name": "Photos",
"value": "'PHOTOS'",
"tags": [],
"docs": "Pick an existing photo from the gallery or photo album."
}
]
},
{
"name": "CameraDirection",
"slug": "cameradirection",
"members": [
{
"name": "Rear",
"value": "'REAR'",
"tags": [],
"docs": ""
},
{
"name": "Front",
"value": "'FRONT'",
"tags": [],
"docs": ""
}
]
}
],
"typeAliases": [
{
"name": "CameraPermissionState",
"slug": "camerapermissionstate",
"docs": "",
"types": [
{
"text": "PermissionState",
"complexTypes": [
"PermissionState"
]
},
{
"text": "'limited'",
"complexTypes": []
}
]
},
{
"name": "PermissionState",
"slug": "permissionstate",
"docs": "",
"types": [
{
"text": "'prompt'",
"complexTypes": []
},
{
"text": "'prompt-with-rationale'",
"complexTypes": []
},
{
"text": "'granted'",
"complexTypes": []
},
{
"text": "'denied'",
"complexTypes": []
}
]
},
{
"name": "CameraPermissionType",
"slug": "camerapermissiontype",
"docs": "",
"types": [
{
"text": "'camera'",
"complexTypes": []
},
{
"text": "'photos'",
"complexTypes": []
}
]
}
],
"pluginConfigs": []
}

View File

@@ -0,0 +1,337 @@
import type { PermissionState } from '@capacitor/core';
export declare type CameraPermissionState = PermissionState | 'limited';
export declare type CameraPermissionType = 'camera' | 'photos';
export interface PermissionStatus {
camera: CameraPermissionState;
photos: CameraPermissionState;
}
export interface CameraPluginPermissions {
permissions: CameraPermissionType[];
}
export interface CameraPlugin {
/**
* Prompt the user to pick a photo from an album, or take a new photo
* with the camera.
*
* @since 1.0.0
*/
getPhoto(options: ImageOptions): Promise<Photo>;
/**
* Allows the user to pick multiple pictures from the photo gallery.
* On iOS 13 and older it only allows to pick one picture.
*
* @since 1.2.0
*/
pickImages(options: GalleryImageOptions): Promise<GalleryPhotos>;
/**
* iOS 14+ Only: Allows the user to update their limited photo library selection.
* On iOS 15+ returns all the limited photos after the picker dismissal.
* On iOS 14 or if the user gave full access to the photos it returns an empty array.
*
* @since 4.1.0
*/
pickLimitedLibraryPhotos(): Promise<GalleryPhotos>;
/**
* iOS 14+ Only: Return an array of photos selected from the limited photo library.
*
* @since 4.1.0
*/
getLimitedLibraryPhotos(): Promise<GalleryPhotos>;
/**
* Check camera and photo album permissions
*
* @since 1.0.0
*/
checkPermissions(): Promise<PermissionStatus>;
/**
* Request camera and photo album permissions
*
* @since 1.0.0
*/
requestPermissions(permissions?: CameraPluginPermissions): Promise<PermissionStatus>;
}
export interface ImageOptions {
/**
* The quality of image to return as JPEG, from 0-100
* Note: This option is only supported on Android and iOS
*
* @since 1.0.0
*/
quality?: number;
/**
* Whether to allow the user to crop or make small edits (platform specific).
* On iOS 14+ it's only supported for CameraSource.Camera, but not for CameraSource.Photos.
*
* @since 1.0.0
*/
allowEditing?: boolean;
/**
* How the data should be returned. Currently, only 'Base64', 'DataUrl' or 'Uri' is supported
*
* @since 1.0.0
*/
resultType: CameraResultType;
/**
* Whether to save the photo to the gallery.
* If the photo was picked from the gallery, it will only be saved if edited.
* @default: false
*
* @since 1.0.0
*/
saveToGallery?: boolean;
/**
* The desired maximum width of the saved image. The aspect ratio is respected.
*
* @since 1.0.0
*/
width?: number;
/**
* The desired maximum height of the saved image. The aspect ratio is respected.
*
* @since 1.0.0
*/
height?: number;
/**
* Whether to automatically rotate the image "up" to correct for orientation
* in portrait mode
* @default: true
*
* @since 1.0.0
*/
correctOrientation?: boolean;
/**
* The source to get the photo from. By default this prompts the user to select
* either the photo album or take a photo.
* @default: CameraSource.Prompt
*
* @since 1.0.0
*/
source?: CameraSource;
/**
* iOS and Web only: The camera direction.
* @default: CameraDirection.Rear
*
* @since 1.0.0
*/
direction?: CameraDirection;
/**
* iOS only: The presentation style of the Camera.
* @default: 'fullscreen'
*
* @since 1.0.0
*/
presentationStyle?: 'fullscreen' | 'popover';
/**
* Web only: Whether to use the PWA Element experience or file input. The
* default is to use PWA Elements if installed and fall back to file input.
* To always use file input, set this to `true`.
*
* Learn more about PWA Elements: https://capacitorjs.com/docs/web/pwa-elements
*
* @since 1.0.0
*/
webUseInput?: boolean;
/**
* Text value to use when displaying the prompt.
* @default: 'Photo'
*
* @since 1.0.0
*
*/
promptLabelHeader?: string;
/**
* Text value to use when displaying the prompt.
* iOS only: The label of the 'cancel' button.
* @default: 'Cancel'
*
* @since 1.0.0
*/
promptLabelCancel?: string;
/**
* Text value to use when displaying the prompt.
* The label of the button to select a saved image.
* @default: 'From Photos'
*
* @since 1.0.0
*/
promptLabelPhoto?: string;
/**
* Text value to use when displaying the prompt.
* The label of the button to open the camera.
* @default: 'Take Picture'
*
* @since 1.0.0
*/
promptLabelPicture?: string;
}
export interface Photo {
/**
* The base64 encoded string representation of the image, if using CameraResultType.Base64.
*
* @since 1.0.0
*/
base64String?: string;
/**
* The url starting with 'data:image/jpeg;base64,' and the base64 encoded string representation of the image, if using CameraResultType.DataUrl.
*
* Note: On web, the file format could change depending on the browser.
* @since 1.0.0
*/
dataUrl?: string;
/**
* If using CameraResultType.Uri, the path will contain a full,
* platform-specific file URL that can be read later using the Filesystem API.
*
* @since 1.0.0
*/
path?: string;
/**
* webPath returns a path that can be used to set the src attribute of an image for efficient
* loading and rendering.
*
* @since 1.0.0
*/
webPath?: string;
/**
* Exif data, if any, retrieved from the image
*
* @since 1.0.0
*/
exif?: any;
/**
* The format of the image, ex: jpeg, png, gif.
*
* iOS and Android only support jpeg.
* Web supports jpeg, png and gif, but the exact availability may vary depending on the browser.
* gif is only supported if `webUseInput` is set to `true` or if `source` is set to `Photos`.
*
* @since 1.0.0
*/
format: string;
/**
* Whether if the image was saved to the gallery or not.
*
* On Android and iOS, saving to the gallery can fail if the user didn't
* grant the required permissions.
* On Web there is no gallery, so always returns false.
*
* @since 1.1.0
*/
saved: boolean;
}
export interface GalleryPhotos {
/**
* Array of all the picked photos.
*
* @since 1.2.0
*/
photos: GalleryPhoto[];
}
export interface GalleryPhoto {
/**
* Full, platform-specific file URL that can be read later using the Filesystem API.
*
* @since 1.2.0
*/
path?: string;
/**
* webPath returns a path that can be used to set the src attribute of an image for efficient
* loading and rendering.
*
* @since 1.2.0
*/
webPath: string;
/**
* Exif data, if any, retrieved from the image
*
* @since 1.2.0
*/
exif?: any;
/**
* The format of the image, ex: jpeg, png, gif.
*
* iOS and Android only support jpeg.
* Web supports jpeg, png and gif.
*
* @since 1.2.0
*/
format: string;
}
export interface GalleryImageOptions {
/**
* The quality of image to return as JPEG, from 0-100
* Note: This option is only supported on Android and iOS.
*
* @since 1.2.0
*/
quality?: number;
/**
* The desired maximum width of the saved image. The aspect ratio is respected.
*
* @since 1.2.0
*/
width?: number;
/**
* The desired maximum height of the saved image. The aspect ratio is respected.
*
* @since 1.2.0
*/
height?: number;
/**
* Whether to automatically rotate the image "up" to correct for orientation
* in portrait mode
* @default: true
*
* @since 1.2.0
*/
correctOrientation?: boolean;
/**
* iOS only: The presentation style of the Camera.
* @default: 'fullscreen'
*
* @since 1.2.0
*/
presentationStyle?: 'fullscreen' | 'popover';
/**
* Maximum number of pictures the user will be able to choose.
* Note: This option is only supported on Android 13+ and iOS.
*
* @default 0 (unlimited)
*
* @since 1.2.0
*/
limit?: number;
}
export declare enum CameraSource {
/**
* Prompts the user to select either the photo album or take a photo.
*/
Prompt = "PROMPT",
/**
* Take a new photo using the camera.
*/
Camera = "CAMERA",
/**
* Pick an existing photo from the gallery or photo album.
*/
Photos = "PHOTOS"
}
export declare enum CameraDirection {
Rear = "REAR",
Front = "FRONT"
}
export declare enum CameraResultType {
Uri = "uri",
Base64 = "base64",
DataUrl = "dataUrl"
}
/**
* @deprecated Use `Photo`.
* @since 1.0.0
*/
export declare type CameraPhoto = Photo;
/**
* @deprecated Use `ImageOptions`.
* @since 1.0.0
*/
export declare type CameraOptions = ImageOptions;

27
node_modules/@capacitor/camera/dist/esm/definitions.js generated vendored Normal file
View File

@@ -0,0 +1,27 @@
export var CameraSource;
(function (CameraSource) {
/**
* Prompts the user to select either the photo album or take a photo.
*/
CameraSource["Prompt"] = "PROMPT";
/**
* Take a new photo using the camera.
*/
CameraSource["Camera"] = "CAMERA";
/**
* Pick an existing photo from the gallery or photo album.
*/
CameraSource["Photos"] = "PHOTOS";
})(CameraSource || (CameraSource = {}));
export var CameraDirection;
(function (CameraDirection) {
CameraDirection["Rear"] = "REAR";
CameraDirection["Front"] = "FRONT";
})(CameraDirection || (CameraDirection = {}));
export var CameraResultType;
(function (CameraResultType) {
CameraResultType["Uri"] = "uri";
CameraResultType["Base64"] = "base64";
CameraResultType["DataUrl"] = "dataUrl";
})(CameraResultType || (CameraResultType = {}));
//# sourceMappingURL=definitions.js.map

File diff suppressed because one or more lines are too long

4
node_modules/@capacitor/camera/dist/esm/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import type { CameraPlugin } from './definitions';
declare const Camera: CameraPlugin;
export * from './definitions';
export { Camera };

8
node_modules/@capacitor/camera/dist/esm/index.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { registerPlugin } from '@capacitor/core';
import { CameraWeb } from './web';
const Camera = registerPlugin('Camera', {
web: () => new CameraWeb(),
});
export * from './definitions';
export { Camera };
//# sourceMappingURL=index.js.map

1
node_modules/@capacitor/camera/dist/esm/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,MAAM,MAAM,GAAG,cAAc,CAAe,QAAQ,EAAE;IACpD,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,SAAS,EAAE;CAC3B,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { CameraPlugin } from './definitions';\nimport { CameraWeb } from './web';\n\nconst Camera = registerPlugin<CameraPlugin>('Camera', {\n web: () => new CameraWeb(),\n});\n\nexport * from './definitions';\nexport { Camera };\n"]}

16
node_modules/@capacitor/camera/dist/esm/web.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import { WebPlugin } from '@capacitor/core';
import type { CameraPlugin, GalleryImageOptions, GalleryPhotos, ImageOptions, PermissionStatus, Photo } from './definitions';
export declare class CameraWeb extends WebPlugin implements CameraPlugin {
getPhoto(options: ImageOptions): Promise<Photo>;
pickImages(_options: GalleryImageOptions): Promise<GalleryPhotos>;
private cameraExperience;
private fileInputExperience;
private multipleFileInputExperience;
private _getCameraPhoto;
checkPermissions(): Promise<PermissionStatus>;
requestPermissions(): Promise<PermissionStatus>;
pickLimitedLibraryPhotos(): Promise<GalleryPhotos>;
getLimitedLibraryPhotos(): Promise<GalleryPhotos>;
}
declare const Camera: CameraWeb;
export { Camera };

254
node_modules/@capacitor/camera/dist/esm/web.js generated vendored Normal file
View File

@@ -0,0 +1,254 @@
import { WebPlugin, CapacitorException } from '@capacitor/core';
import { CameraSource, CameraDirection } from './definitions';
export class CameraWeb extends WebPlugin {
async getPhoto(options) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
if (options.webUseInput || options.source === CameraSource.Photos) {
this.fileInputExperience(options, resolve, reject);
}
else if (options.source === CameraSource.Prompt) {
let actionSheet = document.querySelector('pwa-action-sheet');
if (!actionSheet) {
actionSheet = document.createElement('pwa-action-sheet');
document.body.appendChild(actionSheet);
}
actionSheet.header = options.promptLabelHeader || 'Photo';
actionSheet.cancelable = false;
actionSheet.options = [
{ title: options.promptLabelPhoto || 'From Photos' },
{ title: options.promptLabelPicture || 'Take Picture' },
];
actionSheet.addEventListener('onSelection', async (e) => {
const selection = e.detail;
if (selection === 0) {
this.fileInputExperience(options, resolve, reject);
}
else {
this.cameraExperience(options, resolve, reject);
}
});
}
else {
this.cameraExperience(options, resolve, reject);
}
});
}
async pickImages(_options) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
this.multipleFileInputExperience(resolve, reject);
});
}
async cameraExperience(options, resolve, reject) {
if (customElements.get('pwa-camera-modal')) {
const cameraModal = document.createElement('pwa-camera-modal');
cameraModal.facingMode =
options.direction === CameraDirection.Front ? 'user' : 'environment';
document.body.appendChild(cameraModal);
try {
await cameraModal.componentOnReady();
cameraModal.addEventListener('onPhoto', async (e) => {
const photo = e.detail;
if (photo === null) {
reject(new CapacitorException('User cancelled photos app'));
}
else if (photo instanceof Error) {
reject(photo);
}
else {
resolve(await this._getCameraPhoto(photo, options));
}
cameraModal.dismiss();
document.body.removeChild(cameraModal);
});
cameraModal.present();
}
catch (e) {
this.fileInputExperience(options, resolve, reject);
}
}
else {
console.error(`Unable to load PWA Element 'pwa-camera-modal'. See the docs: https://capacitorjs.com/docs/web/pwa-elements.`);
this.fileInputExperience(options, resolve, reject);
}
}
fileInputExperience(options, resolve, reject) {
let input = document.querySelector('#_capacitor-camera-input');
const cleanup = () => {
var _a;
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
};
if (!input) {
input = document.createElement('input');
input.id = '_capacitor-camera-input';
input.type = 'file';
input.hidden = true;
document.body.appendChild(input);
input.addEventListener('change', (_e) => {
const file = input.files[0];
let format = 'jpeg';
if (file.type === 'image/png') {
format = 'png';
}
else if (file.type === 'image/gif') {
format = 'gif';
}
if (options.resultType === 'dataUrl' ||
options.resultType === 'base64') {
const reader = new FileReader();
reader.addEventListener('load', () => {
if (options.resultType === 'dataUrl') {
resolve({
dataUrl: reader.result,
format,
});
}
else if (options.resultType === 'base64') {
const b64 = reader.result.split(',')[1];
resolve({
base64String: b64,
format,
});
}
cleanup();
});
reader.readAsDataURL(file);
}
else {
resolve({
webPath: URL.createObjectURL(file),
format: format,
});
cleanup();
}
});
input.addEventListener('cancel', (_e) => {
reject(new CapacitorException('User cancelled photos app'));
cleanup();
});
}
input.accept = 'image/*';
input.capture = true;
if (options.source === CameraSource.Photos ||
options.source === CameraSource.Prompt) {
input.removeAttribute('capture');
}
else if (options.direction === CameraDirection.Front) {
input.capture = 'user';
}
else if (options.direction === CameraDirection.Rear) {
input.capture = 'environment';
}
input.click();
}
multipleFileInputExperience(resolve, reject) {
let input = document.querySelector('#_capacitor-camera-input-multiple');
const cleanup = () => {
var _a;
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
};
if (!input) {
input = document.createElement('input');
input.id = '_capacitor-camera-input-multiple';
input.type = 'file';
input.hidden = true;
input.multiple = true;
document.body.appendChild(input);
input.addEventListener('change', (_e) => {
const photos = [];
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < input.files.length; i++) {
const file = input.files[i];
let format = 'jpeg';
if (file.type === 'image/png') {
format = 'png';
}
else if (file.type === 'image/gif') {
format = 'gif';
}
photos.push({
webPath: URL.createObjectURL(file),
format: format,
});
}
resolve({ photos });
cleanup();
});
input.addEventListener('cancel', (_e) => {
reject(new CapacitorException('User cancelled photos app'));
cleanup();
});
}
input.accept = 'image/*';
input.click();
}
_getCameraPhoto(photo, options) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const format = photo.type.split('/')[1];
if (options.resultType === 'uri') {
resolve({
webPath: URL.createObjectURL(photo),
format: format,
saved: false,
});
}
else {
reader.readAsDataURL(photo);
reader.onloadend = () => {
const r = reader.result;
if (options.resultType === 'dataUrl') {
resolve({
dataUrl: r,
format: format,
saved: false,
});
}
else {
resolve({
base64String: r.split(',')[1],
format: format,
saved: false,
});
}
};
reader.onerror = e => {
reject(e);
};
}
});
}
async checkPermissions() {
if (typeof navigator === 'undefined' || !navigator.permissions) {
throw this.unavailable('Permissions API not available in this browser');
}
try {
// https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query
// the specific permissions that are supported varies among browsers that implement the
// permissions API, so we need a try/catch in case 'camera' is invalid
const permission = await window.navigator.permissions.query({
name: 'camera',
});
return {
camera: permission.state,
photos: 'granted',
};
}
catch (_a) {
throw this.unavailable('Camera permissions are not available in this browser');
}
}
async requestPermissions() {
throw this.unimplemented('Not implemented on web.');
}
async pickLimitedLibraryPhotos() {
throw this.unavailable('Not implemented on web.');
}
async getLimitedLibraryPhotos() {
throw this.unavailable('Not implemented on web.');
}
}
const Camera = new CameraWeb();
export { Camera };
//# sourceMappingURL=web.js.map

1
node_modules/@capacitor/camera/dist/esm/web.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

288
node_modules/@capacitor/camera/dist/plugin.cjs.js generated vendored Normal file
View File

@@ -0,0 +1,288 @@
'use strict';
var core = require('@capacitor/core');
exports.CameraSource = void 0;
(function (CameraSource) {
/**
* Prompts the user to select either the photo album or take a photo.
*/
CameraSource["Prompt"] = "PROMPT";
/**
* Take a new photo using the camera.
*/
CameraSource["Camera"] = "CAMERA";
/**
* Pick an existing photo from the gallery or photo album.
*/
CameraSource["Photos"] = "PHOTOS";
})(exports.CameraSource || (exports.CameraSource = {}));
exports.CameraDirection = void 0;
(function (CameraDirection) {
CameraDirection["Rear"] = "REAR";
CameraDirection["Front"] = "FRONT";
})(exports.CameraDirection || (exports.CameraDirection = {}));
exports.CameraResultType = void 0;
(function (CameraResultType) {
CameraResultType["Uri"] = "uri";
CameraResultType["Base64"] = "base64";
CameraResultType["DataUrl"] = "dataUrl";
})(exports.CameraResultType || (exports.CameraResultType = {}));
class CameraWeb extends core.WebPlugin {
async getPhoto(options) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
if (options.webUseInput || options.source === exports.CameraSource.Photos) {
this.fileInputExperience(options, resolve, reject);
}
else if (options.source === exports.CameraSource.Prompt) {
let actionSheet = document.querySelector('pwa-action-sheet');
if (!actionSheet) {
actionSheet = document.createElement('pwa-action-sheet');
document.body.appendChild(actionSheet);
}
actionSheet.header = options.promptLabelHeader || 'Photo';
actionSheet.cancelable = false;
actionSheet.options = [
{ title: options.promptLabelPhoto || 'From Photos' },
{ title: options.promptLabelPicture || 'Take Picture' },
];
actionSheet.addEventListener('onSelection', async (e) => {
const selection = e.detail;
if (selection === 0) {
this.fileInputExperience(options, resolve, reject);
}
else {
this.cameraExperience(options, resolve, reject);
}
});
}
else {
this.cameraExperience(options, resolve, reject);
}
});
}
async pickImages(_options) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
this.multipleFileInputExperience(resolve, reject);
});
}
async cameraExperience(options, resolve, reject) {
if (customElements.get('pwa-camera-modal')) {
const cameraModal = document.createElement('pwa-camera-modal');
cameraModal.facingMode =
options.direction === exports.CameraDirection.Front ? 'user' : 'environment';
document.body.appendChild(cameraModal);
try {
await cameraModal.componentOnReady();
cameraModal.addEventListener('onPhoto', async (e) => {
const photo = e.detail;
if (photo === null) {
reject(new core.CapacitorException('User cancelled photos app'));
}
else if (photo instanceof Error) {
reject(photo);
}
else {
resolve(await this._getCameraPhoto(photo, options));
}
cameraModal.dismiss();
document.body.removeChild(cameraModal);
});
cameraModal.present();
}
catch (e) {
this.fileInputExperience(options, resolve, reject);
}
}
else {
console.error(`Unable to load PWA Element 'pwa-camera-modal'. See the docs: https://capacitorjs.com/docs/web/pwa-elements.`);
this.fileInputExperience(options, resolve, reject);
}
}
fileInputExperience(options, resolve, reject) {
let input = document.querySelector('#_capacitor-camera-input');
const cleanup = () => {
var _a;
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
};
if (!input) {
input = document.createElement('input');
input.id = '_capacitor-camera-input';
input.type = 'file';
input.hidden = true;
document.body.appendChild(input);
input.addEventListener('change', (_e) => {
const file = input.files[0];
let format = 'jpeg';
if (file.type === 'image/png') {
format = 'png';
}
else if (file.type === 'image/gif') {
format = 'gif';
}
if (options.resultType === 'dataUrl' ||
options.resultType === 'base64') {
const reader = new FileReader();
reader.addEventListener('load', () => {
if (options.resultType === 'dataUrl') {
resolve({
dataUrl: reader.result,
format,
});
}
else if (options.resultType === 'base64') {
const b64 = reader.result.split(',')[1];
resolve({
base64String: b64,
format,
});
}
cleanup();
});
reader.readAsDataURL(file);
}
else {
resolve({
webPath: URL.createObjectURL(file),
format: format,
});
cleanup();
}
});
input.addEventListener('cancel', (_e) => {
reject(new core.CapacitorException('User cancelled photos app'));
cleanup();
});
}
input.accept = 'image/*';
input.capture = true;
if (options.source === exports.CameraSource.Photos ||
options.source === exports.CameraSource.Prompt) {
input.removeAttribute('capture');
}
else if (options.direction === exports.CameraDirection.Front) {
input.capture = 'user';
}
else if (options.direction === exports.CameraDirection.Rear) {
input.capture = 'environment';
}
input.click();
}
multipleFileInputExperience(resolve, reject) {
let input = document.querySelector('#_capacitor-camera-input-multiple');
const cleanup = () => {
var _a;
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
};
if (!input) {
input = document.createElement('input');
input.id = '_capacitor-camera-input-multiple';
input.type = 'file';
input.hidden = true;
input.multiple = true;
document.body.appendChild(input);
input.addEventListener('change', (_e) => {
const photos = [];
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < input.files.length; i++) {
const file = input.files[i];
let format = 'jpeg';
if (file.type === 'image/png') {
format = 'png';
}
else if (file.type === 'image/gif') {
format = 'gif';
}
photos.push({
webPath: URL.createObjectURL(file),
format: format,
});
}
resolve({ photos });
cleanup();
});
input.addEventListener('cancel', (_e) => {
reject(new core.CapacitorException('User cancelled photos app'));
cleanup();
});
}
input.accept = 'image/*';
input.click();
}
_getCameraPhoto(photo, options) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const format = photo.type.split('/')[1];
if (options.resultType === 'uri') {
resolve({
webPath: URL.createObjectURL(photo),
format: format,
saved: false,
});
}
else {
reader.readAsDataURL(photo);
reader.onloadend = () => {
const r = reader.result;
if (options.resultType === 'dataUrl') {
resolve({
dataUrl: r,
format: format,
saved: false,
});
}
else {
resolve({
base64String: r.split(',')[1],
format: format,
saved: false,
});
}
};
reader.onerror = e => {
reject(e);
};
}
});
}
async checkPermissions() {
if (typeof navigator === 'undefined' || !navigator.permissions) {
throw this.unavailable('Permissions API not available in this browser');
}
try {
// https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query
// the specific permissions that are supported varies among browsers that implement the
// permissions API, so we need a try/catch in case 'camera' is invalid
const permission = await window.navigator.permissions.query({
name: 'camera',
});
return {
camera: permission.state,
photos: 'granted',
};
}
catch (_a) {
throw this.unavailable('Camera permissions are not available in this browser');
}
}
async requestPermissions() {
throw this.unimplemented('Not implemented on web.');
}
async pickLimitedLibraryPhotos() {
throw this.unavailable('Not implemented on web.');
}
async getLimitedLibraryPhotos() {
throw this.unavailable('Not implemented on web.');
}
}
new CameraWeb();
const Camera = core.registerPlugin('Camera', {
web: () => new CameraWeb(),
});
exports.Camera = Camera;
//# sourceMappingURL=plugin.cjs.js.map

File diff suppressed because one or more lines are too long

291
node_modules/@capacitor/camera/dist/plugin.js generated vendored Normal file
View File

@@ -0,0 +1,291 @@
var capacitorCamera = (function (exports, core) {
'use strict';
exports.CameraSource = void 0;
(function (CameraSource) {
/**
* Prompts the user to select either the photo album or take a photo.
*/
CameraSource["Prompt"] = "PROMPT";
/**
* Take a new photo using the camera.
*/
CameraSource["Camera"] = "CAMERA";
/**
* Pick an existing photo from the gallery or photo album.
*/
CameraSource["Photos"] = "PHOTOS";
})(exports.CameraSource || (exports.CameraSource = {}));
exports.CameraDirection = void 0;
(function (CameraDirection) {
CameraDirection["Rear"] = "REAR";
CameraDirection["Front"] = "FRONT";
})(exports.CameraDirection || (exports.CameraDirection = {}));
exports.CameraResultType = void 0;
(function (CameraResultType) {
CameraResultType["Uri"] = "uri";
CameraResultType["Base64"] = "base64";
CameraResultType["DataUrl"] = "dataUrl";
})(exports.CameraResultType || (exports.CameraResultType = {}));
class CameraWeb extends core.WebPlugin {
async getPhoto(options) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
if (options.webUseInput || options.source === exports.CameraSource.Photos) {
this.fileInputExperience(options, resolve, reject);
}
else if (options.source === exports.CameraSource.Prompt) {
let actionSheet = document.querySelector('pwa-action-sheet');
if (!actionSheet) {
actionSheet = document.createElement('pwa-action-sheet');
document.body.appendChild(actionSheet);
}
actionSheet.header = options.promptLabelHeader || 'Photo';
actionSheet.cancelable = false;
actionSheet.options = [
{ title: options.promptLabelPhoto || 'From Photos' },
{ title: options.promptLabelPicture || 'Take Picture' },
];
actionSheet.addEventListener('onSelection', async (e) => {
const selection = e.detail;
if (selection === 0) {
this.fileInputExperience(options, resolve, reject);
}
else {
this.cameraExperience(options, resolve, reject);
}
});
}
else {
this.cameraExperience(options, resolve, reject);
}
});
}
async pickImages(_options) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
this.multipleFileInputExperience(resolve, reject);
});
}
async cameraExperience(options, resolve, reject) {
if (customElements.get('pwa-camera-modal')) {
const cameraModal = document.createElement('pwa-camera-modal');
cameraModal.facingMode =
options.direction === exports.CameraDirection.Front ? 'user' : 'environment';
document.body.appendChild(cameraModal);
try {
await cameraModal.componentOnReady();
cameraModal.addEventListener('onPhoto', async (e) => {
const photo = e.detail;
if (photo === null) {
reject(new core.CapacitorException('User cancelled photos app'));
}
else if (photo instanceof Error) {
reject(photo);
}
else {
resolve(await this._getCameraPhoto(photo, options));
}
cameraModal.dismiss();
document.body.removeChild(cameraModal);
});
cameraModal.present();
}
catch (e) {
this.fileInputExperience(options, resolve, reject);
}
}
else {
console.error(`Unable to load PWA Element 'pwa-camera-modal'. See the docs: https://capacitorjs.com/docs/web/pwa-elements.`);
this.fileInputExperience(options, resolve, reject);
}
}
fileInputExperience(options, resolve, reject) {
let input = document.querySelector('#_capacitor-camera-input');
const cleanup = () => {
var _a;
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
};
if (!input) {
input = document.createElement('input');
input.id = '_capacitor-camera-input';
input.type = 'file';
input.hidden = true;
document.body.appendChild(input);
input.addEventListener('change', (_e) => {
const file = input.files[0];
let format = 'jpeg';
if (file.type === 'image/png') {
format = 'png';
}
else if (file.type === 'image/gif') {
format = 'gif';
}
if (options.resultType === 'dataUrl' ||
options.resultType === 'base64') {
const reader = new FileReader();
reader.addEventListener('load', () => {
if (options.resultType === 'dataUrl') {
resolve({
dataUrl: reader.result,
format,
});
}
else if (options.resultType === 'base64') {
const b64 = reader.result.split(',')[1];
resolve({
base64String: b64,
format,
});
}
cleanup();
});
reader.readAsDataURL(file);
}
else {
resolve({
webPath: URL.createObjectURL(file),
format: format,
});
cleanup();
}
});
input.addEventListener('cancel', (_e) => {
reject(new core.CapacitorException('User cancelled photos app'));
cleanup();
});
}
input.accept = 'image/*';
input.capture = true;
if (options.source === exports.CameraSource.Photos ||
options.source === exports.CameraSource.Prompt) {
input.removeAttribute('capture');
}
else if (options.direction === exports.CameraDirection.Front) {
input.capture = 'user';
}
else if (options.direction === exports.CameraDirection.Rear) {
input.capture = 'environment';
}
input.click();
}
multipleFileInputExperience(resolve, reject) {
let input = document.querySelector('#_capacitor-camera-input-multiple');
const cleanup = () => {
var _a;
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
};
if (!input) {
input = document.createElement('input');
input.id = '_capacitor-camera-input-multiple';
input.type = 'file';
input.hidden = true;
input.multiple = true;
document.body.appendChild(input);
input.addEventListener('change', (_e) => {
const photos = [];
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < input.files.length; i++) {
const file = input.files[i];
let format = 'jpeg';
if (file.type === 'image/png') {
format = 'png';
}
else if (file.type === 'image/gif') {
format = 'gif';
}
photos.push({
webPath: URL.createObjectURL(file),
format: format,
});
}
resolve({ photos });
cleanup();
});
input.addEventListener('cancel', (_e) => {
reject(new core.CapacitorException('User cancelled photos app'));
cleanup();
});
}
input.accept = 'image/*';
input.click();
}
_getCameraPhoto(photo, options) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const format = photo.type.split('/')[1];
if (options.resultType === 'uri') {
resolve({
webPath: URL.createObjectURL(photo),
format: format,
saved: false,
});
}
else {
reader.readAsDataURL(photo);
reader.onloadend = () => {
const r = reader.result;
if (options.resultType === 'dataUrl') {
resolve({
dataUrl: r,
format: format,
saved: false,
});
}
else {
resolve({
base64String: r.split(',')[1],
format: format,
saved: false,
});
}
};
reader.onerror = e => {
reject(e);
};
}
});
}
async checkPermissions() {
if (typeof navigator === 'undefined' || !navigator.permissions) {
throw this.unavailable('Permissions API not available in this browser');
}
try {
// https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query
// the specific permissions that are supported varies among browsers that implement the
// permissions API, so we need a try/catch in case 'camera' is invalid
const permission = await window.navigator.permissions.query({
name: 'camera',
});
return {
camera: permission.state,
photos: 'granted',
};
}
catch (_a) {
throw this.unavailable('Camera permissions are not available in this browser');
}
}
async requestPermissions() {
throw this.unimplemented('Not implemented on web.');
}
async pickLimitedLibraryPhotos() {
throw this.unavailable('Not implemented on web.');
}
async getLimitedLibraryPhotos() {
throw this.unavailable('Not implemented on web.');
}
}
new CameraWeb();
const Camera = core.registerPlugin('Camera', {
web: () => new CameraWeb(),
});
exports.Camera = Camera;
return exports;
})({}, capacitorExports);
//# sourceMappingURL=plugin.js.map

1
node_modules/@capacitor/camera/dist/plugin.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,100 @@
import UIKit
import Photos
internal protocol CameraAuthorizationState {
var authorizationState: String { get }
}
extension AVAuthorizationStatus: CameraAuthorizationState {
var authorizationState: String {
switch self {
case .denied, .restricted:
return "denied"
case .authorized:
return "granted"
case .notDetermined:
fallthrough
@unknown default:
return "prompt"
}
}
}
extension PHAuthorizationStatus: CameraAuthorizationState {
var authorizationState: String {
switch self {
case .denied, .restricted:
return "denied"
case .authorized:
return "granted"
case .limited:
return "limited"
case .notDetermined:
fallthrough
@unknown default:
return "prompt"
}
}
}
internal extension PHAsset {
/**
Retrieves the image metadata for the asset.
*/
var imageData: [String: Any] {
let options = PHImageRequestOptions()
options.isSynchronous = true
options.resizeMode = .none
options.isNetworkAccessAllowed = false
options.version = .current
var result: [String: Any] = [:]
_ = PHCachingImageManager().requestImageDataAndOrientation(for: self, options: options) { (data, _, _, _) in
if let data = data as NSData? {
let options = [kCGImageSourceShouldCache as String: kCFBooleanFalse] as CFDictionary
if let imgSrc = CGImageSourceCreateWithData(data, options),
let metadata = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options) as? [String: Any] {
result = metadata
}
}
}
return result
}
}
internal extension UIImage {
/**
Generates a new image from the existing one, implicitly resetting any orientation.
Dimensions greater than 0 will resize the image while preserving the aspect ratio.
*/
func reformat(to size: CGSize? = nil) -> UIImage {
let imageHeight = self.size.height
let imageWidth = self.size.width
// determine the max dimensions, 0 is treated as 'no restriction'
var maxWidth: CGFloat
if let size = size, size.width > 0 {
maxWidth = size.width
} else {
maxWidth = imageWidth
}
let maxHeight: CGFloat
if let size = size, size.height > 0 {
maxHeight = size.height
} else {
maxHeight = imageHeight
}
// adjust to preserve aspect ratio
var targetWidth = min(imageWidth, maxWidth)
var targetHeight = (imageHeight * targetWidth) / imageWidth
if targetHeight > maxHeight {
targetWidth = (imageWidth * maxHeight) / imageHeight
targetHeight = maxHeight
}
// generate the new image and return
UIGraphicsBeginImageContextWithOptions(.init(width: targetWidth, height: targetHeight), false, 1.0) // size, opaque and scale
self.draw(in: .init(origin: .zero, size: .init(width: targetWidth, height: targetHeight)))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage ?? self
}
}

View File

@@ -0,0 +1,584 @@
import Foundation
import Capacitor
import Photos
import PhotosUI
@objc(CAPCameraPlugin)
public class CameraPlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "CAPCameraPlugin"
public let jsName = "Camera"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "getPhoto", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "pickImages", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "pickLimitedLibraryPhotos", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "getLimitedLibraryPhotos", returnType: CAPPluginReturnPromise)
]
private var call: CAPPluginCall?
private var settings = CameraSettings()
private let defaultSource = CameraSource.prompt
private let defaultDirection = CameraDirection.rear
private var multiple = false
private var imageCounter = 0
@objc override public func checkPermissions(_ call: CAPPluginCall) {
var result: [String: Any] = [:]
for permission in CameraPermissionType.allCases {
let state: String
switch permission {
case .camera:
state = AVCaptureDevice.authorizationStatus(for: .video).authorizationState
case .photos:
if #available(iOS 14, *) {
state = PHPhotoLibrary.authorizationStatus(for: .readWrite).authorizationState
} else {
state = PHPhotoLibrary.authorizationStatus().authorizationState
}
}
result[permission.rawValue] = state
}
call.resolve(result)
}
@objc override public func requestPermissions(_ call: CAPPluginCall) {
// get the list of desired types, if passed
let typeList = call.getArray("permissions", String.self)?.compactMap({ (type) -> CameraPermissionType? in
return CameraPermissionType(rawValue: type)
}) ?? []
// otherwise check everything
let permissions: [CameraPermissionType] = (typeList.count > 0) ? typeList : CameraPermissionType.allCases
// request the permissions
let group = DispatchGroup()
for permission in permissions {
switch permission {
case .camera:
group.enter()
AVCaptureDevice.requestAccess(for: .video) { _ in
group.leave()
}
case .photos:
group.enter()
if #available(iOS 14, *) {
PHPhotoLibrary.requestAuthorization(for: .readWrite) { (_) in
group.leave()
}
} else {
PHPhotoLibrary.requestAuthorization({ (_) in
group.leave()
})
}
}
}
group.notify(queue: DispatchQueue.main) { [weak self] in
self?.checkPermissions(call)
}
}
@objc func pickLimitedLibraryPhotos(_ call: CAPPluginCall) {
if #available(iOS 14, *) {
PHPhotoLibrary.requestAuthorization(for: .readWrite) { (granted) in
if granted == .limited {
if let viewController = self.bridge?.viewController {
if #available(iOS 15, *) {
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController) { _ in
self.getLimitedLibraryPhotos(call)
}
} else {
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController)
call.resolve([
"photos": []
])
}
}
} else {
call.resolve([
"photos": []
])
}
}
} else {
call.unavailable("Not available on iOS 13")
}
}
@objc func getLimitedLibraryPhotos(_ call: CAPPluginCall) {
if #available(iOS 14, *) {
PHPhotoLibrary.requestAuthorization(for: .readWrite) { (granted) in
if granted == .limited {
self.call = call
DispatchQueue.global(qos: .utility).async {
let assets = PHAsset.fetchAssets(with: .image, options: nil)
var processedImages: [ProcessedImage] = []
let imageManager = PHImageManager.default()
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
let group = DispatchGroup()
if assets.count > 0 {
for index in 0...(assets.count - 1) {
let asset = assets.object(at: index)
let fullSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
group.enter()
imageManager.requestImage(for: asset, targetSize: fullSize, contentMode: .default, options: options) { image, _ in
guard let image = image else {
group.leave()
return
}
processedImages.append(self.processedImage(from: image, with: asset.imageData))
group.leave()
}
}
}
group.notify(queue: .global(qos: .utility)) { [weak self] in
self?.returnImages(processedImages)
}
}
} else {
call.resolve([
"photos": []
])
}
}
} else {
call.unavailable("Not available on iOS 13")
}
}
@objc func getPhoto(_ call: CAPPluginCall) {
self.multiple = false
self.call = call
self.settings = cameraSettings(from: call)
// Make sure they have all the necessary info.plist settings
if let missingUsageDescription = checkUsageDescriptions() {
CAPLog.print("⚡️ ", self.pluginId, "-", missingUsageDescription)
call.reject(missingUsageDescription)
return
}
DispatchQueue.main.async {
switch self.settings.source {
case .prompt:
self.showPrompt()
case .camera:
self.showCamera()
case .photos:
self.showPhotos()
}
}
}
@objc func pickImages(_ call: CAPPluginCall) {
self.multiple = true
self.call = call
self.settings = cameraSettings(from: call)
DispatchQueue.main.async {
self.showPhotos()
}
}
private func checkUsageDescriptions() -> String? {
if let dict = Bundle.main.infoDictionary {
for key in CameraPropertyListKeys.allCases where dict[key.rawValue] == nil {
return key.missingMessage
}
}
return nil
}
private func cameraSettings(from call: CAPPluginCall) -> CameraSettings {
var settings = CameraSettings()
settings.jpegQuality = min(abs(CGFloat(call.getFloat("quality") ?? 100.0)) / 100.0, 1.0)
settings.allowEditing = call.getBool("allowEditing") ?? false
settings.source = CameraSource(rawValue: call.getString("source") ?? defaultSource.rawValue) ?? defaultSource
settings.direction = CameraDirection(rawValue: call.getString("direction") ?? defaultDirection.rawValue) ?? defaultDirection
if let typeString = call.getString("resultType"), let type = CameraResultType(rawValue: typeString) {
settings.resultType = type
}
settings.saveToGallery = call.getBool("saveToGallery") ?? false
// Get the new image dimensions if provided
settings.width = CGFloat(call.getInt("width") ?? 0)
settings.height = CGFloat(call.getInt("height") ?? 0)
if settings.width > 0 || settings.height > 0 {
// We resize only if a dimension was provided
settings.shouldResize = true
}
settings.shouldCorrectOrientation = call.getBool("correctOrientation") ?? true
settings.userPromptText = CameraPromptText(title: call.getString("promptLabelHeader"),
photoAction: call.getString("promptLabelPhoto"),
cameraAction: call.getString("promptLabelPicture"),
cancelAction: call.getString("promptLabelCancel"))
if let styleString = call.getString("presentationStyle"), styleString == "popover" {
settings.presentationStyle = .popover
} else {
settings.presentationStyle = .fullScreen
}
return settings
}
}
// public delegate methods
extension CameraPlugin: UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIPopoverPresentationControllerDelegate {
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true)
self.call?.reject("User cancelled photos app")
}
public func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
self.call?.reject("User cancelled photos app")
}
public func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
self.call?.reject("User cancelled photos app")
}
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
picker.dismiss(animated: true) {
if let processedImage = self.processImage(from: info) {
self.returnProcessedImage(processedImage)
} else {
self.call?.reject("Error processing image")
}
}
}
}
@available(iOS 14, *)
extension CameraPlugin: PHPickerViewControllerDelegate {
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard !results.isEmpty else {
self.call?.reject("User cancelled photos app")
return
}
self.fetchProcessedImages(from: results) { [weak self] processedImageArray in
guard let processedImageArray else {
self?.call?.reject("Error loading image")
return
}
if self?.multiple == true {
self?.returnImages(processedImageArray)
} else if var processedImage = processedImageArray.first {
processedImage.flags = .gallery
self?.returnProcessedImage(processedImage)
}
}
}
private func fetchProcessedImages(from pickerResultArray: [PHPickerResult], accumulating: [ProcessedImage] = [], _ completionHandler: @escaping ([ProcessedImage]?) -> Void) {
func loadImage(from pickerResult: PHPickerResult, _ completionHandler: @escaping (UIImage?) -> Void) {
let itemProvider = pickerResult.itemProvider
if itemProvider.canLoadObject(ofClass: UIImage.self) {
// extract the image
itemProvider.loadObject(ofClass: UIImage.self) { itemProviderReading, _ in
completionHandler(itemProviderReading as? UIImage)
}
} else {
// extract the image's data representation
itemProvider.loadDataRepresentation(forTypeIdentifier: UTType.image.identifier) { data, _ in
guard let data else {
return completionHandler(nil)
}
completionHandler(UIImage(data: data))
}
}
}
guard let currentPickerResult = pickerResultArray.first else { return completionHandler(accumulating) }
loadImage(from: currentPickerResult) { [weak self] loadedImage in
guard let self, let loadedImage else { return completionHandler(nil) }
var asset: PHAsset?
if let assetId = currentPickerResult.assetIdentifier {
asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil).firstObject
}
let newElement = self.processedImage(from: loadedImage, with: asset?.imageData)
self.fetchProcessedImages(
from: Array(pickerResultArray.dropFirst()),
accumulating: accumulating + [newElement],
completionHandler
)
}
}
}
private extension CameraPlugin {
func returnImage(_ processedImage: ProcessedImage, isSaved: Bool) {
guard let jpeg = processedImage.generateJPEG(with: settings.jpegQuality) else {
self.call?.reject("Unable to convert image to jpeg")
return
}
if settings.resultType == CameraResultType.uri || multiple {
guard let fileURL = try? saveTemporaryImage(jpeg),
let webURL = bridge?.portablePath(fromLocalURL: fileURL) else {
call?.reject("Unable to get portable path to file")
return
}
if self.multiple {
call?.resolve([
"photos": [[
"path": fileURL.absoluteString,
"exif": processedImage.exifData,
"webPath": webURL.absoluteString,
"format": "jpeg"
]]
])
return
}
call?.resolve([
"path": fileURL.absoluteString,
"exif": processedImage.exifData,
"webPath": webURL.absoluteString,
"format": "jpeg",
"saved": isSaved
])
} else if settings.resultType == CameraResultType.base64 {
self.call?.resolve([
"base64String": jpeg.base64EncodedString(),
"exif": processedImage.exifData,
"format": "jpeg",
"saved": isSaved
])
} else if settings.resultType == CameraResultType.dataURL {
call?.resolve([
"dataUrl": "data:image/jpeg;base64," + jpeg.base64EncodedString(),
"exif": processedImage.exifData,
"format": "jpeg",
"saved": isSaved
])
}
}
func returnImages(_ processedImages: [ProcessedImage]) {
var photos: [PluginCallResultData] = []
for processedImage in processedImages {
guard let jpeg = processedImage.generateJPEG(with: settings.jpegQuality) else {
self.call?.reject("Unable to convert image to jpeg")
return
}
guard let fileURL = try? saveTemporaryImage(jpeg),
let webURL = bridge?.portablePath(fromLocalURL: fileURL) else {
call?.reject("Unable to get portable path to file")
return
}
photos.append([
"path": fileURL.absoluteString,
"exif": processedImage.exifData,
"webPath": webURL.absoluteString,
"format": "jpeg"
])
}
call?.resolve([
"photos": photos
])
}
func returnProcessedImage(_ processedImage: ProcessedImage) {
// conditionally save the image
if settings.saveToGallery && (processedImage.flags.contains(.edited) == true || processedImage.flags.contains(.gallery) == false) {
_ = ImageSaver(image: processedImage.image) { error in
var isSaved = false
if error == nil {
isSaved = true
}
self.returnImage(processedImage, isSaved: isSaved)
}
} else {
self.returnImage(processedImage, isSaved: false)
}
}
func showPrompt() {
// Build the action sheet
let alert = UIAlertController(title: settings.userPromptText.title, message: nil, preferredStyle: UIAlertController.Style.actionSheet)
alert.addAction(UIAlertAction(title: settings.userPromptText.photoAction, style: .default, handler: { [weak self] (_: UIAlertAction) in
self?.showPhotos()
}))
alert.addAction(UIAlertAction(title: settings.userPromptText.cameraAction, style: .default, handler: { [weak self] (_: UIAlertAction) in
self?.showCamera()
}))
alert.addAction(UIAlertAction(title: settings.userPromptText.cancelAction, style: .cancel, handler: { [weak self] (_: UIAlertAction) in
self?.call?.reject("User cancelled photos app")
}))
self.setCenteredPopover(alert)
self.bridge?.viewController?.present(alert, animated: true, completion: nil)
}
func showCamera() {
// check if we have a camera
if (bridge?.isSimEnvironment ?? false) || !UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) {
CAPLog.print("⚡️ ", self.pluginId, "-", "Camera not available in simulator")
call?.reject("Camera not available while running in Simulator")
return
}
// check for permission
let authStatus = AVCaptureDevice.authorizationStatus(for: .video)
if authStatus == .restricted || authStatus == .denied {
call?.reject("User denied access to camera")
return
}
// we either already have permission or can prompt
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
if granted {
DispatchQueue.main.async {
self?.presentCameraPicker()
}
} else {
self?.call?.reject("User denied access to camera")
}
}
}
func showPhotos() {
// check for permission
let authStatus = PHPhotoLibrary.authorizationStatus()
if authStatus == .restricted || authStatus == .denied {
call?.reject("User denied access to photos")
return
}
// we either already have permission or can prompt
if authStatus == .authorized {
presentSystemAppropriateImagePicker()
} else {
PHPhotoLibrary.requestAuthorization({ [weak self] (status) in
if status == PHAuthorizationStatus.authorized {
DispatchQueue.main.async { [weak self] in
self?.presentSystemAppropriateImagePicker()
}
} else {
self?.call?.reject("User denied access to photos")
}
})
}
}
func presentCameraPicker() {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = self.settings.allowEditing
// select the input
picker.sourceType = .camera
if settings.direction == .rear, UIImagePickerController.isCameraDeviceAvailable(.rear) {
picker.cameraDevice = .rear
} else if settings.direction == .front, UIImagePickerController.isCameraDeviceAvailable(.front) {
picker.cameraDevice = .front
}
// present
picker.modalPresentationStyle = settings.presentationStyle
if settings.presentationStyle == .popover {
picker.popoverPresentationController?.delegate = self
setCenteredPopover(picker)
}
bridge?.viewController?.present(picker, animated: true, completion: nil)
}
func presentSystemAppropriateImagePicker() {
if #available(iOS 14, *) {
presentPhotoPicker()
} else {
presentImagePicker()
}
}
func presentImagePicker() {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = self.settings.allowEditing
// select the input
picker.sourceType = .photoLibrary
// present
picker.modalPresentationStyle = settings.presentationStyle
if settings.presentationStyle == .popover {
picker.popoverPresentationController?.delegate = self
setCenteredPopover(picker)
}
bridge?.viewController?.present(picker, animated: true, completion: nil)
}
@available(iOS 14, *)
func presentPhotoPicker() {
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.selectionLimit = self.multiple ? (self.call?.getInt("limit") ?? 0) : 1
configuration.filter = .images
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
// present
picker.modalPresentationStyle = settings.presentationStyle
if settings.presentationStyle == .popover {
picker.popoverPresentationController?.delegate = self
setCenteredPopover(picker)
}
bridge?.viewController?.present(picker, animated: true, completion: nil)
}
func saveTemporaryImage(_ data: Data) throws -> URL {
var url: URL
repeat {
imageCounter += 1
url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("photo-\(imageCounter).jpg")
} while FileManager.default.fileExists(atPath: url.path)
try data.write(to: url, options: .atomic)
return url
}
func processImage(from info: [UIImagePickerController.InfoKey: Any]) -> ProcessedImage? {
var selectedImage: UIImage?
var flags: PhotoFlags = []
// get the image
if let edited = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
selectedImage = edited // use the edited version
flags = flags.union([.edited])
} else if let original = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
selectedImage = original // use the original version
}
guard let image = selectedImage else {
return nil
}
var metadata: [String: Any] = [:]
// get the image's metadata from the picker or from the photo album
if let photoMetadata = info[UIImagePickerController.InfoKey.mediaMetadata] as? [String: Any] {
metadata = photoMetadata
} else {
flags = flags.union([.gallery])
}
if let asset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset {
metadata = asset.imageData
}
// get the result
var result = processedImage(from: image, with: metadata)
result.flags = flags
return result
}
func processedImage(from image: UIImage, with metadata: [String: Any]?) -> ProcessedImage {
var result = ProcessedImage(image: image, metadata: metadata ?? [:])
// resizing the image only makes sense if we have real values to which to constrain it
if settings.shouldResize, settings.width > 0 || settings.height > 0 {
result.image = result.image.reformat(to: CGSize(width: settings.width, height: settings.height))
result.overwriteMetadataOrientation(to: 1)
} else if settings.shouldCorrectOrientation {
// resizing implicitly reformats the image so this is only needed if we aren't resizing
result.image = result.image.reformat()
result.overwriteMetadataOrientation(to: 1)
}
return result
}
}

View File

@@ -0,0 +1,142 @@
import UIKit
// MARK: - Public
public enum CameraSource: String {
case prompt = "PROMPT"
case camera = "CAMERA"
case photos = "PHOTOS"
}
public enum CameraDirection: String {
case rear = "REAR"
case front = "FRONT"
}
public enum CameraResultType: String {
case base64
case uri
case dataURL = "dataUrl"
}
struct CameraPromptText {
let title: String
let photoAction: String
let cameraAction: String
let cancelAction: String
init(title: String? = nil, photoAction: String? = nil, cameraAction: String? = nil, cancelAction: String? = nil) {
self.title = title ?? "Photo"
self.photoAction = photoAction ?? "From Photos"
self.cameraAction = cameraAction ?? "Take Picture"
self.cancelAction = cancelAction ?? "Cancel"
}
}
public struct CameraSettings {
var source: CameraSource = CameraSource.prompt
var direction: CameraDirection = CameraDirection.rear
var resultType = CameraResultType.base64
var userPromptText = CameraPromptText()
var jpegQuality: CGFloat = 1.0
var width: CGFloat = 0
var height: CGFloat = 0
var allowEditing = false
var shouldResize = false
var shouldCorrectOrientation = true
var saveToGallery = false
var presentationStyle = UIModalPresentationStyle.fullScreen
}
public struct CameraResult {
let image: UIImage?
let metadata: [AnyHashable: Any]
}
// MARK: - Internal
internal enum CameraPermissionType: String, CaseIterable {
case camera
case photos
}
internal enum CameraPropertyListKeys: String, CaseIterable {
case photoLibraryAddUsage = "NSPhotoLibraryAddUsageDescription"
case photoLibraryUsage = "NSPhotoLibraryUsageDescription"
case cameraUsage = "NSCameraUsageDescription"
var link: String {
switch self {
case .photoLibraryAddUsage:
return "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW73"
case .photoLibraryUsage:
return "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW17"
case .cameraUsage:
return "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW24"
}
}
var missingMessage: String {
return "You are missing \(self.rawValue) in your Info.plist file." +
" Camera will not function without it. Learn more: \(self.link)"
}
}
internal struct PhotoFlags: OptionSet {
let rawValue: Int
static let edited = PhotoFlags(rawValue: 1 << 0)
static let gallery = PhotoFlags(rawValue: 1 << 1)
static let all: PhotoFlags = [.edited, .gallery]
}
internal struct ProcessedImage {
var image: UIImage
var metadata: [String: Any]
var flags: PhotoFlags = []
var exifData: [String: Any] {
var exifData = metadata["{Exif}"] as? [String: Any]
exifData?["Orientation"] = metadata["Orientation"]
exifData?["GPS"] = metadata["{GPS}"]
return exifData ?? [:]
}
mutating func overwriteMetadataOrientation(to orientation: Int) {
replaceDictionaryOrientation(atNode: &metadata, to: orientation)
}
func replaceDictionaryOrientation(atNode node: inout [String: Any], to orientation: Int) {
for key in node.keys {
if key == "Orientation", (node[key] as? Int) != nil {
node[key] = orientation
} else if var child = node[key] as? [String: Any] {
replaceDictionaryOrientation(atNode: &child, to: orientation)
node[key] = child
}
}
}
func generateJPEG(with quality: CGFloat) -> Data? {
// convert the UIImage to a jpeg
guard let data = self.image.jpegData(compressionQuality: quality) else {
return nil
}
// define our jpeg data as an image source and get its type
guard let source = CGImageSourceCreateWithData(data as CFData, nil), let type = CGImageSourceGetType(source) else {
return data
}
// allocate an output buffer and create the destination to receive the new data
guard let output = NSMutableData(capacity: data.count), let destination = CGImageDestinationCreateWithData(output, type, 1, nil) else {
return data
}
// pipe the source into the destination while overwriting the metadata, this encodes the metadata information into the image
CGImageDestinationAddImageFromSource(destination, source, 0, self.metadata as CFDictionary)
// finish
guard CGImageDestinationFinalize(destination) else {
return data
}
return output as Data
}
}

View File

@@ -0,0 +1,20 @@
import UIKit
class ImageSaver: NSObject {
var onResult: ((Error?) -> Void) = {_ in }
init(image: UIImage, onResult: @escaping ((Error?) -> Void)) {
self.onResult = onResult
super.init()
UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveResult), nil)
}
@objc func saveResult(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let error = error {
onResult(error)
} else {
onResult(nil)
}
}
}

View File

@@ -0,0 +1,12 @@
import XCTest
@testable import CameraPlugin
final class CameraPluginTests: XCTestCase {
func testExample() throws {
// XCTest Documentation
// https://developer.apple.com/documentation/xctest
// Defining Test Cases and Test Methods
// https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
}
}

85
node_modules/@capacitor/camera/package.json generated vendored Normal file
View File

@@ -0,0 +1,85 @@
{
"name": "@capacitor/camera",
"version": "7.0.2",
"description": "The Camera API provides the ability to take a photo with the camera or choose an existing one from the photo album.",
"main": "dist/plugin.cjs.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"unpkg": "dist/plugin.js",
"files": [
"android/src/main/",
"android/build.gradle",
"dist/",
"ios/Sources",
"ios/Tests",
"Package.swift",
"CapacitorCamera.podspec"
],
"author": "Ionic <hi@ionicframework.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/ionic-team/capacitor-plugins.git"
},
"bugs": {
"url": "https://github.com/ionic-team/capacitor-plugins/issues"
},
"keywords": [
"capacitor",
"plugin",
"native"
],
"scripts": {
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
"verify:ios": "xcodebuild build -scheme CapacitorCamera -destination generic/platform=iOS",
"verify:android": "cd android && ./gradlew clean build test && cd ..",
"verify:web": "npm run build",
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
"eslint": "eslint . --ext ts",
"prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
"swiftlint": "node-swiftlint",
"docgen": "docgen --api CameraPlugin --output-readme README.md --output-json dist/docs.json",
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
"clean": "rimraf ./dist",
"watch": "tsc --watch",
"prepublishOnly": "npm run build",
"publish:cocoapod": "pod trunk push ./CapacitorCamera.podspec --allow-warnings"
},
"devDependencies": {
"@capacitor/android": "^7.0.0",
"@capacitor/core": "^7.0.0",
"@capacitor/docgen": "0.2.2",
"@capacitor/ios": "^7.0.0",
"@ionic/eslint-config": "^0.4.0",
"@ionic/prettier-config": "~1.0.1",
"@ionic/swiftlint-config": "^1.1.2",
"eslint": "^8.57.0",
"prettier": "~2.3.0",
"prettier-plugin-java": "~1.0.2",
"rimraf": "^6.0.1",
"rollup": "^4.26.0",
"swiftlint": "^1.0.1",
"typescript": "~4.1.5"
},
"peerDependencies": {
"@capacitor/core": ">=7.0.0"
},
"prettier": "@ionic/prettier-config",
"swiftlint": "@ionic/swiftlint-config",
"eslintConfig": {
"extends": "@ionic/eslint-config/recommended"
},
"capacitor": {
"ios": {
"src": "ios"
},
"android": {
"src": "android"
}
},
"publishConfig": {
"access": "public"
},
"gitHead": "7c53a4c249abe958983f1ac9df04dec7ab89c10e"
}