Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 47 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ the system's image library.

Although the object is attached to the global scoped `navigator`, it is not available until after the `deviceready` event.

document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(navigator.camera);
}
```js
document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {
console.log(navigator.camera);
}
```

## Installation

Expand Down Expand Up @@ -65,41 +67,37 @@ In order for your changes to be accepted, you need to sign and submit an Apache

**And don't forget to test and document your code.**

### iOS Quirks
### iOS Specifics

Since iOS 10 it's mandatory to provide an usage description in the `info.plist` if trying to access privacy-sensitive data. When the system prompts the user to allow access, this usage description string will displayed as part of the permission dialog box, but if you didn't provide the usage description, the app will crash before showing the dialog. Also, Apple will reject apps that access private data but don't provide an usage description.
Since iOS 10 it's mandatory to provide a usage description in the `info.plist` when accessing privacy-sensitive data. The required keys depend on how you use the plugin and which iOS versions you support:

This plugins requires the following usage descriptions:
| Key | Description |
| ------------------------------ | ----------- |
| NSCameraUsageDescription | Required whenever the camera is used (e.g. `Camera.PictureSourceType.CAMERA`). |
| NSPhotoLibraryUsageDescription | Required only when your app runs on iOS 13 or older and using as `sourceType` `Camera.PictureSourceType.PHOTOLIBRARY`. On iOS 14+ the plugin uses PHPicker for read-only access, which does not need this key. |
| NSPhotoLibraryAddUsageDescription | Required when the plugin writes to the user's library (e.g. `saveToPhotoAlbum=true`). |
| NSLocationWhenInUseUsageDescription | Required if `CameraUsesGeolocation` is set to `true`. |

- `NSCameraUsageDescription` specifies the reason for your app to access the device's camera.
- `NSPhotoLibraryUsageDescription` specifies the reason for your app to access the user's photo library.
- `NSLocationWhenInUseUsageDescription` specifies the reason for your app to access the user's location information while your app is in use. (Set it if you have `CameraUsesGeolocation` preference set to `true`)
- `NSPhotoLibraryAddUsageDescription` specifies the reason for your app to get write-only access to the user's photo library
When the system prompts the user to allow access, this usage description string will be displayed as part of the permission dialog box. If you don't provide the required usage description, the app will crash before showing the dialog. Also, Apple will reject apps that access private data but don't provide a usage description.

To add these entries into the `info.plist`, you can use the `edit-config` tag in the `config.xml` like this:

```
```xml
<edit-config target="NSCameraUsageDescription" file="*-Info.plist" mode="merge">
<string>need camera access to take pictures</string>
</edit-config>
```

```
<edit-config target="NSPhotoLibraryUsageDescription" file="*-Info.plist" mode="merge">
<string>need photo library access to get pictures from there</string>
</edit-config>
```

```
<edit-config target="NSLocationWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
<string>need location access to find things nearby</string>
</edit-config>
```

```
<edit-config target="NSPhotoLibraryAddUsageDescription" file="*-Info.plist" mode="merge">
<string>need photo library access to save pictures there</string>
</edit-config>

<edit-config target="NSLocationWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
<string>need location access to find things nearby</string>
</edit-config>
```

---
Expand Down Expand Up @@ -368,9 +366,11 @@ Defines the output format of `Camera.getPicture` call.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| PHOTOLIBRARY | <code>number</code> | <code>0</code> | Choose image from the device's photo library (same as SAVEDPHOTOALBUM for Android) |
| PHOTOLIBRARY | <code>number</code> | <code>0</code> | Choose image from the device's photo library. |
| CAMERA | <code>number</code> | <code>1</code> | Take picture from camera |
| SAVEDPHOTOALBUM | <code>number</code> | <code>2</code> | Choose image only from the device's Camera Roll album (same as PHOTOLIBRARY for Android) |
| SAVEDPHOTOALBUM | <code>number</code> | <code>2</code> | Same as `PHOTOLIBRARY`, when running on Android or iOS 14+. On iOS older than 14, an image can only be chosen from the device's Camera Roll album with this setting. |



<a name="module_Camera.PopoverArrowDirection"></a>

Expand Down Expand Up @@ -643,23 +643,25 @@ function openCamera(selection) {

## Select a File from the Picture Library <a name="selectFile"></a>

When selecting a file using the file picker, you also need to set the CameraOptions object. In this example, set the `sourceType` to `Camera.PictureSourceType.SAVEDPHOTOALBUM`. To open the file picker, call `getPicture` just as you did in the previous example, passing in the success and error callbacks along with CameraOptions object.
When selecting a file using the file picker, you also need to set the CameraOptions object. In this example, set the `sourceType` to `Camera.PictureSourceType.PHOTOLIBRARY`. To open the file picker, call `getPicture` just as you did in the previous example, passing in the success and error callbacks along with CameraOptions object.

```js
function openFilePicker(selection) {

var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
var srcType = Camera.PictureSourceType.PHOTOLIBRARY;
var options = setOptions(srcType);
var func = createNewFileEntry;

navigator.camera.getPicture(function cameraSuccess(imageUri) {

// Do something

}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");

}, options);
navigator.camera.getPicture(
// success callback
(imageUri) => {
// Do something
},
// error callback
(error) => {
console.debug("Unable to obtain picture: " + error, "app");
},
options);
}
```

Expand All @@ -670,7 +672,7 @@ Resizing a file selected with the file picker works just like resizing using the
```js
function openFilePicker(selection) {

var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
var srcType = Camera.PictureSourceType.PHOTOLIBRARY;
var options = setOptions(srcType);
var func = createNewFileEntry;

Expand All @@ -681,14 +683,16 @@ function openFilePicker(selection) {
options.targetWidth = 100;
}

navigator.camera.getPicture(function cameraSuccess(imageUri) {

// Do something with image

}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");

}, options);
navigator.camera.getPicture(
// success callback
(imageUri) {
// Do something with image
},
// error callback
(error) => {
console.debug("Unable to obtain picture: " + error, "app");
},
options);
}
```

Expand Down
15 changes: 11 additions & 4 deletions src/android/CameraLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
private static final int DATA_URL = 0; // Return base64 encoded string
private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android)

private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
private static final int PHOTOLIBRARY = 0; // Choose image from picture library
private static final int CAMERA = 1; // Take picture from camera
private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android)
private static final int SAVEDPHOTOALBUM = 2; // Same as PHOTOLIBRARY. This settings makes only a difference on iOS older than 14,
// where an image can only be chosen from the device's Camera Roll album, with this setting.

private static final int PICTURE = 0; // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
private static final int VIDEO = 1; // allow selection of video only, ONLY RETURNS URL
Expand Down Expand Up @@ -423,9 +424,15 @@ public void getImage(int srcType, int returnType) {
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
}

if (this.cordova != null) {
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
new String(title)), (srcType + 1) * 16 + returnType + 1);
this.cordova.startActivityForResult(
(CordovaPlugin) this,
Intent.createChooser(
intent,
new String(title)),
// Requestcode
(srcType + 1) * 16 + returnType + 1);
}
}

Expand Down
55 changes: 44 additions & 11 deletions src/ios/CDVCamera.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@
#import <CoreLocation/CLLocationManager.h>
#import <Cordova/CDVPlugin.h>

// Since iOS 14, we can use PHPickerViewController to select images from the photo library
//
// The following condition checks if the iOS 14 SDK is available for XCode
// which is true for XCode 12+. It does not check on runtime, if the device is running iOS 14+.
// For that API_AVAILABLE(ios(14)) is used for methods declarations and @available(iOS 14, *) for the code.
// The condition here makes just sure that the code can compile in XCode
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 // Always true on XCode12+

// Import UniformTypeIdentifiers.h for using UTType* things, available since iOS 14,
// which replaces for e.g. kUTTypeImage with UTTypeImage, which must be used in the future instead
// Currently only used for PHPickerViewController
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>

// Import PhotosUI framework for using PHPickerViewController
// PhotosUI is already available since iOS 8, but since we need it currently
// only for the PHPickerViewController, we import it conditionally here
#import <PhotosUI/PhotosUI.h>

#endif

enum CDVDestinationType {
DestinationTypeDataUrl = 0,
DestinationTypeFileUri
Expand Down Expand Up @@ -78,38 +98,51 @@ typedef NSUInteger CDVMediaType;
@end

// ======================================================================= //

// Use PHPickerViewController in iOS 14+ to select images from the photo library
// PHPickerViewControllerDelegate is only available since iOS 14
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 // Always true on XCode12+
@interface CDVCamera : CDVPlugin <UIImagePickerControllerDelegate,
UINavigationControllerDelegate,
UIPopoverControllerDelegate,
CLLocationManagerDelegate,
PHPickerViewControllerDelegate>
{}
#else
@interface CDVCamera : CDVPlugin <UIImagePickerControllerDelegate,
UINavigationControllerDelegate,
UIPopoverControllerDelegate,
CLLocationManagerDelegate>
{}
#endif

@property (strong) CDVCameraPicker* pickerController;
@property (strong) NSMutableDictionary *metadata;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong) NSData* data;

/*
* getPicture
*
* arguments:
* 1: this is the javascript function that will be called with the results, the first parameter passed to the
* javascript function is the picture as a Base64 encoded string
* 2: this is the javascript function to be called if there was an error
* options:
* quality: integer between 1 and 100
*/
- (void)takePicture:(CDVInvokedUrlCommand*)command;
- (void)cleanup:(CDVInvokedUrlCommand*)command;
- (void)repositionPopover:(CDVInvokedUrlCommand*)command;

// UIImagePickerControllerDelegate methods
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info;
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo;
- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker;

// UINavigationControllerDelegate method
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

// CLLocationManagerDelegate methods
- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation;
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error;

// PHPickerViewController specific methods (iOS 14+)
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 // Always true on XCode12+
- (void)showPHPicker:(NSString*)callbackId withOptions:(CDVPictureOptions*)pictureOptions API_AVAILABLE(ios(14));
- (void)processPHPickerImage:(UIImage*)image assetIdentifier:(NSString*)assetIdentifier callbackId:(NSString*)callbackId options:(CDVPictureOptions*)options API_AVAILABLE(ios(14));
- (void)finalizePHPickerImage:(UIImage*)image metadata:(NSDictionary*)metadata callbackId:(NSString*)callbackId options:(CDVPictureOptions*)options API_AVAILABLE(ios(14));
// PHPickerViewControllerDelegate method
- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14));
#endif

@end
Loading
Loading