Adds macOS support

This commit is contained in:
José Expósito 2017-10-10 17:11:10 +02:00
parent b2107e0818
commit 97d2014d62
6 changed files with 3049 additions and 8 deletions

View File

@ -21,9 +21,9 @@ description: Take pictures with the device camera.
# under the License.
-->
|AppVeyor|Travis CI|
|:-:|:-:|
|[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-plugin-camera?branch=master)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-camera)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-camera.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-camera)|
|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI |
|:-:|:-:|:-:|:-:|:-:|:-:|
|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-camera)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-camera/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-camera)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-camera/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-camera)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-camera/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-camera)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-camera/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-camera)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-camera/)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-camera.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-camera)
# cordova-plugin-camera
@ -53,11 +53,11 @@ It is also possible to install via repo url directly ( unstable )
## How to Contribute
Contributors are welcome! And we need your contributions to keep the project moving forward. You can [report bugs](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22cordova-plugin-camera%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC), improve the documentation, or [contribute code](https://github.com/apache/cordova-plugin-camera/pulls).
Contributors are welcome! And we need your contributions to keep the project moving forward. You can [report bugs](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Camera%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC), improve the documentation, or [contribute code](https://github.com/apache/cordova-plugin-camera/pulls).
There is a specific [contributor workflow](http://wiki.apache.org/cordova/ContributorWorkflow) we recommend. Start reading there. More information is available on [our wiki](http://wiki.apache.org/cordova).
:warning: **Found an issue?** File it on [JIRA issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22cordova-plugin-camera%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC).
:warning: **Found an issue?** File it on [JIRA issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Camera%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC).
**Have a solution?** Send a [Pull Request](https://github.com/apache/cordova-plugin-camera/pulls).

2683
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,8 @@
"wp8",
"windows8",
"browser",
"windows"
"windows",
"osx"
]
},
"repository": {
@ -40,7 +41,8 @@
"cordova-wp8",
"cordova-windows8",
"cordova-browser",
"cordova-windows"
"cordova-windows",
"cordova-osx"
],
"scripts": {
"precommit": "npm run gen-docs && git add README.md",

View File

@ -280,6 +280,23 @@
</js-module>
</platform>
<!-- osx -->
<platform name="osx">
<config-file target="config.xml" parent="/*">
<feature name="Camera">
<param name="osx-package" value="CDVCamera"/>
</feature>
</config-file>
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
<clobbers target="CameraPopoverHandle" />
</js-module>
<header-file src="src/osx/CDVCamera.h" />
<source-file src="src/osx/CDVCamera.m" />
<framework src="Quartz.framework" />
<framework src="AppKit.framework" />
</platform>
</plugin>

81
src/osx/CDVCamera.h Normal file
View File

@ -0,0 +1,81 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Quartz/Quartz.h>
#import <AppKit/AppKit.h>
#import <Cordova/CDVPlugin.h>
enum CDVDestinationType {
DestinationTypeDataUrl = 0,
DestinationTypeFileUri,
DestinationTypeNativeUri
};
typedef NSUInteger CDVDestinationType;
enum CDVSourceType {
SourceTypePhotoLibrary = 0,
SourceTypeCamera,
SourceTypePhotoAlbum
};
typedef NSUInteger CDVSourceType;
enum CDVEncodingType {
EncodingTypeJPEG = 0,
EncodingTypePNG
};
typedef NSUInteger CDVEncodingType;
enum CDVMediaType {
MediaTypePicture = 0,
MediaTypeVideo,
MediaTypeAll
};
typedef NSUInteger CDVMediaType;
// ======================================================================= //
@interface CDVPictureOptions : NSObject
@property (strong) NSNumber *quality;
@property (assign) CDVDestinationType destinationType;
@property (assign) CDVSourceType sourceType;
@property (assign) CGSize targetSize;
@property (assign) CDVEncodingType encodingType;
@property (assign) CDVMediaType mediaType;
@property (assign) BOOL allowsEditing;
@property (assign) BOOL correctOrientation;
@property (assign) BOOL saveToPhotoAlbum;
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand *)command;
@end
// ======================================================================= //
@interface CDVCamera : CDVPlugin
- (void)takePicture:(CDVInvokedUrlCommand *)command;
- (void)cleanup:(CDVInvokedUrlCommand *)command;
@end

258
src/osx/CDVCamera.m Normal file
View File

@ -0,0 +1,258 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVCamera.h"
@implementation CDVPictureOptions
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand*)command {
CDVPictureOptions *pictureOptions = [[CDVPictureOptions alloc] init];
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(50)];
pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(SourceTypeCamera)] unsignedIntegerValue];
NSNumber *targetWidth = [command argumentAtIndex:3 withDefault:nil];
NSNumber *targetHeight = [command argumentAtIndex:4 withDefault:nil];
pictureOptions.targetSize = CGSizeMake(0, 0);
if ((targetWidth != nil) && (targetHeight != nil)) {
pictureOptions.targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]);
}
pictureOptions.encodingType = [[command argumentAtIndex:5 withDefault:@(EncodingTypeJPEG)] unsignedIntegerValue];
pictureOptions.mediaType = [[command argumentAtIndex:6 withDefault:@(MediaTypePicture)] unsignedIntegerValue];
pictureOptions.allowsEditing = [[command argumentAtIndex:7 withDefault:@(NO)] boolValue];
pictureOptions.correctOrientation = [[command argumentAtIndex:8 withDefault:@(NO)] boolValue];
pictureOptions.saveToPhotoAlbum = [[command argumentAtIndex:9 withDefault:@(NO)] boolValue];
return pictureOptions;
}
@end
// ======================================================================= //
@implementation CDVCamera
/*!
Static array that stores the temporary created files allowing to delete them when calling navigator.camera.cleanup(...)
*/
static NSMutableArray *cleanUpFiles;
+ (void)initialize {
cleanUpFiles = [NSMutableArray array];
}
- (void)takePicture:(CDVInvokedUrlCommand *)command {
CDVPictureOptions *pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
if (pictureOptions.sourceType == SourceTypeCamera) {
[self takePictureFromCamera:command withOptions:pictureOptions];
} else {
[self takePictureFromFile:command withOptions:pictureOptions];
}
}
- (void)cleanup:(CDVInvokedUrlCommand*)command {
[self.commandDelegate runInBackground:^{
if (cleanUpFiles.count > 0) {
for (int i=0; i<cleanUpFiles.count; i++) {
NSString *path = [cleanUpFiles objectAtIndex:i];
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
}
[cleanUpFiles removeAllObjects];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
}];
}
#pragma mark - Camera
/*!
Takes a picture from the iSight camera using the default OS dialog.
@see https://developer.apple.com/documentation/quartz/ikpicturetaker
*/
- (void)takePictureFromCamera:(CDVInvokedUrlCommand *)command withOptions:(CDVPictureOptions *)pictureOptions {
IKPictureTaker *pictureTaker = [IKPictureTaker pictureTaker];
[pictureTaker setValue:[NSNumber numberWithBool:YES] forKey:IKPictureTakerAllowsVideoCaptureKey];
[pictureTaker setValue:[NSNumber numberWithBool:NO] forKey:IKPictureTakerAllowsFileChoosingKey];
[pictureTaker setValue:[NSNumber numberWithBool:pictureOptions.allowsEditing] forKey:IKPictureTakerShowEffectsKey];
[pictureTaker setValue:[NSNumber numberWithBool:pictureOptions.allowsEditing] forKey:IKPictureTakerAllowsEditingKey];
NSDictionary *contextInfo = @{ @"command": command, @"pictureOptions" : pictureOptions};
[pictureTaker beginPictureTakerSheetForWindow:self.viewController.contentView.window withDelegate:self didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain(contextInfo)];
}
- (void)pictureTakerDidEnd:(IKPictureTaker *)pictureTaker returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
if (returnCode == NSOKButton) {
NSDictionary *contextInfoDictionary = (NSDictionary *)CFBridgingRelease(contextInfo);
CDVInvokedUrlCommand *command = [contextInfoDictionary valueForKey:@"command"];
CDVPictureOptions *pictureOptions = [contextInfoDictionary valueForKey:@"pictureOptions"];
[self returnImage:pictureTaker.outputImage command:command options:pictureOptions];
}
}
#pragma mark - File
/*!
Allows to select an image or video using the system native dialog.
*/
- (void)takePictureFromFile:(CDVInvokedUrlCommand *)command withOptions:(CDVPictureOptions *)pictureOptions {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.canChooseFiles = YES;
openPanel.canChooseDirectories = NO;
openPanel.canCreateDirectories = YES;
openPanel.allowsMultipleSelection = NO;
NSMutableArray *allowedTypes = [NSMutableArray array];
if (pictureOptions.mediaType == MediaTypePicture || pictureOptions.mediaType == MediaTypeAll) {
[allowedTypes addObjectsFromArray:[NSImage imageTypes]];
}
if (pictureOptions.mediaType == MediaTypeVideo || pictureOptions.mediaType == MediaTypeAll) {
[allowedTypes addObjectsFromArray:@[(NSString *)kUTTypeMovie]];
}
[openPanel setAllowedFileTypes:allowedTypes];
[openPanel beginSheetModalForWindow:self.viewController.contentView.window completionHandler:^(NSInteger result) {
if (result == NSOKButton) {
NSURL *fileURL = [openPanel.URLs objectAtIndex:0];
if ([self fileIsImage:fileURL]) {
NSImage *image = [[NSImage alloc] initWithContentsOfFile:fileURL.path];
[self returnImage:image command:command options:pictureOptions];
} else {
if (pictureOptions.destinationType == DestinationTypeDataUrl) {
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Camera.DestinationType.DATA_URL is only available with image files"];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
} else {
[self returnUri:fileURL.path command:command options:pictureOptions];
}
}
}
}];
}
#pragma mark - Common
/*!
Returns to JavaScript a URI.
Called when Camera.DestinationType.FILE_URI or Camera.DestinationType.NATIVE_URI.
*/
- (void)returnUri:(NSString *)path command:(CDVInvokedUrlCommand *)command options:(CDVPictureOptions *)pictureOptions {
NSString *protocol = (pictureOptions.destinationType == DestinationTypeFileUri) ? @"file://" : @"";
NSString *uri = [NSString stringWithFormat:@"%@%@", protocol, path];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:uri];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
/*!
Returns to JavaScript a base64 encoded image.
Called when Camera.DestinationType.DATA_URL.
*/
- (void)returnImage:(NSImage *)image command:(CDVInvokedUrlCommand *)command options:(CDVPictureOptions *)pictureOptions {
[self.commandDelegate runInBackground:^{
NSData *processedImageData = [self processImage:image options:pictureOptions];
if (pictureOptions.destinationType == DestinationTypeDataUrl) {
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[processedImageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
} else {
NSString *tempFilePath = [self uniqueImageName:pictureOptions];
[processedImageData writeToFile:tempFilePath atomically:YES];
[cleanUpFiles addObject:tempFilePath];
[self returnUri:tempFilePath command:command options:pictureOptions];
}
}];
}
/*!
Top level method to apply the size and quality required changes to an image.
*/
- (NSData *)processImage:(NSImage *)image options:(CDVPictureOptions *)pictureOptions {
NSImage *sourceImage = image;
if (pictureOptions.targetSize.width > 0 && pictureOptions.targetSize.height > 0) {
sourceImage = [self resizeImage:sourceImage toSize:pictureOptions.targetSize];
}
CGImageRef cgRef = [sourceImage CGImageForProposedRect:NULL context:nil hints:nil];
NSBitmapImageRep *imageRepresentation = [[NSBitmapImageRep alloc] initWithCGImage:cgRef];
NSData *data = (pictureOptions.encodingType == EncodingTypeJPEG)
? [imageRepresentation representationUsingType:NSJPEGFileType properties:@{NSImageCompressionFactor: [NSNumber numberWithFloat:pictureOptions.quality.floatValue/100.f]}]
: [imageRepresentation representationUsingType:NSPNGFileType properties:@{NSImageCompressionFactor: @1.0}];
return data;
}
/*!
Auxiliar method to resize an image.
*/
- (NSImage *)resizeImage:(NSImage *)image toSize:(CGSize)newSize {
CGFloat aspectWidth = newSize.width / image.size.width;
CGFloat aspectHeight = newSize.height / image.size.height;
CGFloat aspectRatio = MIN(aspectWidth, aspectHeight);
CGSize scaledSize = NSMakeSize(image.size.width*aspectRatio, image.size.height*aspectRatio);
NSImage *smallImage = [[NSImage alloc] initWithSize: scaledSize];
[smallImage lockFocus];
[image setSize: scaledSize];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[image drawAtPoint:NSZeroPoint fromRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height) operation:NSCompositeCopy fraction:1.0];
[smallImage unlockFocus];
return smallImage;
}
/*!
Auxiliar method to know if a given file is an image or not.
*/
- (BOOL)fileIsImage:(NSURL *)fileURL {
NSString *type;
BOOL isImage = NO;
if ([fileURL getResourceValue:&type forKey:NSURLTypeIdentifierKey error:nil]) {
isImage = [[NSImage imageTypes] containsObject:type];
}
return isImage;
}
/*!
Auxiliar method that generates an unique filename for an image in the temporary directory.
*/
- (NSString *)uniqueImageName:(CDVPictureOptions *)pictureOptions {
NSString *tempDir = NSTemporaryDirectory();
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString] ;
NSString *extension = (pictureOptions.encodingType == EncodingTypeJPEG) ? @"jpeg" : @"png";
NSString *uniqueFileName = [NSString stringWithFormat:@"%@%@.%@", tempDir, guid, extension];
return uniqueFileName;
}
@end