arnaucube
arnaucube/openEventsPlatformAppPublic Self-hosted repository
openEventsPlatformApp/plugins/cordova-plugin-camera/src/ios/CDVCamera.m
plugins/cordova-plugin-camera/src/ios/CDVCamera.mRaw
  1/*
  2 Licensed to the Apache Software Foundation (ASF) under one
  3 or more contributor license agreements.  See the NOTICE file
  4 distributed with this work for additional information
  5 regarding copyright ownership.  The ASF licenses this file
  6 to you under the Apache License, Version 2.0 (the
  7 "License"); you may not use this file except in compliance
  8 with the License.  You may obtain a copy of the License at
  9
 10 http://www.apache.org/licenses/LICENSE-2.0
 11
 12 Unless required by applicable law or agreed to in writing,
 13 software distributed under the License is distributed on an
 14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15 KIND, either express or implied.  See the License for the
 16 specific language governing permissions and limitations
 17 under the License.
 18 */
 19
 20#import "CDVCamera.h"
 21#import "CDVJpegHeaderWriter.h"
 22#import "UIImage+CropScaleOrientation.h"
 23#import <ImageIO/CGImageProperties.h>
 24#import <AssetsLibrary/ALAssetRepresentation.h>
 25#import <AssetsLibrary/AssetsLibrary.h>
 26#import <AVFoundation/AVFoundation.h>
 27#import <ImageIO/CGImageSource.h>
 28#import <ImageIO/CGImageProperties.h>
 29#import <ImageIO/CGImageDestination.h>
 30#import <MobileCoreServices/UTCoreTypes.h>
 31#import <objc/message.h>
 32
 33#ifndef __CORDOVA_4_0_0
 34    #import <Cordova/NSData+Base64.h>
 35#endif
 36
 37#define CDV_PHOTO_PREFIX @"cdv_photo_"
 38
 39static NSSet* org_apache_cordova_validArrowDirections;
 40
 41static NSString* toBase64(NSData* data) {
 42    SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
 43    SEL s2 = NSSelectorFromString(@"base64EncodedString");
 44    SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
 45    
 46    if ([data respondsToSelector:s1]) {
 47        NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
 48        return func(data, s1);
 49    } else if ([data respondsToSelector:s2]) {
 50        NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s2];
 51        return func(data, s2);
 52    } else if ([data respondsToSelector:s3]) {
 53        NSString* (*func)(id, SEL, NSUInteger) = (void *)[data methodForSelector:s3];
 54        return func(data, s3, 0);
 55    } else {
 56        return nil;
 57    }
 58}
 59
 60@implementation CDVPictureOptions
 61
 62+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand*)command
 63{
 64    CDVPictureOptions* pictureOptions = [[CDVPictureOptions alloc] init];
 65
 66    pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(50)];
 67    pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
 68    pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(UIImagePickerControllerSourceTypeCamera)] unsignedIntegerValue];
 69    
 70    NSNumber* targetWidth = [command argumentAtIndex:3 withDefault:nil];
 71    NSNumber* targetHeight = [command argumentAtIndex:4 withDefault:nil];
 72    pictureOptions.targetSize = CGSizeMake(0, 0);
 73    if ((targetWidth != nil) && (targetHeight != nil)) {
 74        pictureOptions.targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]);
 75    }
 76
 77    pictureOptions.encodingType = [[command argumentAtIndex:5 withDefault:@(EncodingTypeJPEG)] unsignedIntegerValue];
 78    pictureOptions.mediaType = [[command argumentAtIndex:6 withDefault:@(MediaTypePicture)] unsignedIntegerValue];
 79    pictureOptions.allowsEditing = [[command argumentAtIndex:7 withDefault:@(NO)] boolValue];
 80    pictureOptions.correctOrientation = [[command argumentAtIndex:8 withDefault:@(NO)] boolValue];
 81    pictureOptions.saveToPhotoAlbum = [[command argumentAtIndex:9 withDefault:@(NO)] boolValue];
 82    pictureOptions.popoverOptions = [command argumentAtIndex:10 withDefault:nil];
 83    pictureOptions.cameraDirection = [[command argumentAtIndex:11 withDefault:@(UIImagePickerControllerCameraDeviceRear)] unsignedIntegerValue];
 84    
 85    pictureOptions.popoverSupported = NO;
 86    pictureOptions.usesGeolocation = NO;
 87    
 88    return pictureOptions;
 89}
 90
 91@end
 92
 93
 94@interface CDVCamera ()
 95
 96@property (readwrite, assign) BOOL hasPendingOperation;
 97
 98@end
 99
100@implementation CDVCamera
101
102+ (void)initialize
103{
104    org_apache_cordova_validArrowDirections = [[NSSet alloc] initWithObjects:[NSNumber numberWithInt:UIPopoverArrowDirectionUp], [NSNumber numberWithInt:UIPopoverArrowDirectionDown], [NSNumber numberWithInt:UIPopoverArrowDirectionLeft], [NSNumber numberWithInt:UIPopoverArrowDirectionRight], [NSNumber numberWithInt:UIPopoverArrowDirectionAny], nil];
105}
106
107@synthesize hasPendingOperation, pickerController, locationManager;
108
109- (NSURL*) urlTransformer:(NSURL*)url
110{
111    NSURL* urlToTransform = url;
112    
113    // for backwards compatibility - we check if this property is there
114    SEL sel = NSSelectorFromString(@"urlTransformer");
115    if ([self.commandDelegate respondsToSelector:sel]) {
116        // grab the block from the commandDelegate
117        NSURL* (^urlTransformer)(NSURL*) = ((id(*)(id, SEL))objc_msgSend)(self.commandDelegate, sel);
118        // if block is not null, we call it
119        if (urlTransformer) {
120            urlToTransform = urlTransformer(url);
121        }
122    }
123    
124    return urlToTransform;
125}
126
127- (BOOL)usesGeolocation
128{
129    id useGeo = [self.commandDelegate.settings objectForKey:[@"CameraUsesGeolocation" lowercaseString]];
130    return [(NSNumber*)useGeo boolValue];
131}
132
133- (BOOL)popoverSupported
134{
135    return (NSClassFromString(@"UIPopoverController") != nil) &&
136           (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
137}
138
139- (void)takePicture:(CDVInvokedUrlCommand*)command
140{
141    self.hasPendingOperation = YES;
142    
143    __weak CDVCamera* weakSelf = self;
144
145    [self.commandDelegate runInBackground:^{
146        
147        CDVPictureOptions* pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
148        pictureOptions.popoverSupported = [weakSelf popoverSupported];
149        pictureOptions.usesGeolocation = [weakSelf usesGeolocation];
150        pictureOptions.cropToSize = NO;
151        
152        BOOL hasCamera = [UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType];
153        if (!hasCamera) {
154            NSLog(@"Camera.getPicture: source type %lu not available.", (unsigned long)pictureOptions.sourceType);
155            CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No camera available"];
156            [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
157            return;
158        }
159
160        // Validate the app has permission to access the camera
161        if (pictureOptions.sourceType == UIImagePickerControllerSourceTypeCamera && [AVCaptureDevice respondsToSelector:@selector(authorizationStatusForMediaType:)]) {
162            AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
163            if (authStatus == AVAuthorizationStatusDenied ||
164                authStatus == AVAuthorizationStatusRestricted) {
165                // If iOS 8+, offer a link to the Settings app
166#pragma clang diagnostic push
167#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
168                NSString* settingsButton = (&UIApplicationOpenSettingsURLString != NULL)
169                    ? NSLocalizedString(@"Settings", nil)
170                    : nil;
171#pragma clang diagnostic pop
172
173                // Denied; show an alert
174                dispatch_async(dispatch_get_main_queue(), ^{
175                    [[[UIAlertView alloc] initWithTitle:[[NSBundle mainBundle]
176                                                         objectForInfoDictionaryKey:@"CFBundleDisplayName"]
177                                                message:NSLocalizedString(@"Access to the camera has been prohibited; please enable it in the Settings app to continue.", nil)
178                                               delegate:weakSelf
179                                      cancelButtonTitle:NSLocalizedString(@"OK", nil)
180                                      otherButtonTitles:settingsButton, nil] show];
181                });
182            }
183        }
184
185        CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
186        weakSelf.pickerController = cameraPicker;
187        
188        cameraPicker.delegate = weakSelf;
189        cameraPicker.callbackId = command.callbackId;
190        // we need to capture this state for memory warnings that dealloc this object
191        cameraPicker.webView = weakSelf.webView;
192        
193        // Perform UI operations on the main thread
194        dispatch_async(dispatch_get_main_queue(), ^{
195            // If a popover is already open, close it; we only want one at a time.
196            if (([[weakSelf pickerController] pickerPopoverController] != nil) && [[[weakSelf pickerController] pickerPopoverController] isPopoverVisible]) {
197                [[[weakSelf pickerController] pickerPopoverController] dismissPopoverAnimated:YES];
198                [[[weakSelf pickerController] pickerPopoverController] setDelegate:nil];
199                [[weakSelf pickerController] setPickerPopoverController:nil];
200            }
201
202            if ([weakSelf popoverSupported] && (pictureOptions.sourceType != UIImagePickerControllerSourceTypeCamera)) {
203                if (cameraPicker.pickerPopoverController == nil) {
204                    cameraPicker.pickerPopoverController = [[NSClassFromString(@"UIPopoverController") alloc] initWithContentViewController:cameraPicker];
205                }
206                [weakSelf displayPopover:pictureOptions.popoverOptions];
207                weakSelf.hasPendingOperation = NO;
208            } else {
209                [weakSelf.viewController presentViewController:cameraPicker animated:YES completion:^{
210                    weakSelf.hasPendingOperation = NO;
211                }];
212            }
213        });
214    }];
215}
216
217// Delegate for camera permission UIAlertView
218- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
219{
220    // If Settings button (on iOS 8), open the settings app
221    if (buttonIndex == 1) {
222#pragma clang diagnostic push
223#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
224        if (&UIApplicationOpenSettingsURLString != NULL) {
225            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
226        }
227#pragma clang diagnostic pop
228    }
229
230    // Dismiss the view
231    [[self.pickerController presentingViewController] dismissViewControllerAnimated:YES completion:nil];
232
233    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to camera"];   // error callback expects string ATM
234
235    [self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId];
236
237    self.hasPendingOperation = NO;
238    self.pickerController = nil;
239}
240
241- (void)repositionPopover:(CDVInvokedUrlCommand*)command
242{
243    if (([[self pickerController] pickerPopoverController] != nil) && [[[self pickerController] pickerPopoverController] isPopoverVisible]) {
244
245        [[[self pickerController] pickerPopoverController] dismissPopoverAnimated:NO];
246
247        NSDictionary* options = [command argumentAtIndex:0 withDefault:nil];
248        [self displayPopover:options];
249    }
250}
251
252- (NSInteger)integerValueForKey:(NSDictionary*)dict key:(NSString*)key defaultValue:(NSInteger)defaultValue
253{
254    NSInteger value = defaultValue;
255
256    NSNumber* val = [dict valueForKey:key];  // value is an NSNumber
257
258    if (val != nil) {
259        value = [val integerValue];
260    }
261    return value;
262}
263
264- (void)displayPopover:(NSDictionary*)options
265{
266    NSInteger x = 0;
267    NSInteger y = 32;
268    NSInteger width = 320;
269    NSInteger height = 480;
270    UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
271
272    if (options) {
273        x = [self integerValueForKey:options key:@"x" defaultValue:0];
274        y = [self integerValueForKey:options key:@"y" defaultValue:32];
275        width = [self integerValueForKey:options key:@"width" defaultValue:320];
276        height = [self integerValueForKey:options key:@"height" defaultValue:480];
277        arrowDirection = [self integerValueForKey:options key:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny];
278        if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithUnsignedInteger:arrowDirection]]) {
279            arrowDirection = UIPopoverArrowDirectionAny;
280        }
281    }
282
283    [[[self pickerController] pickerPopoverController] setDelegate:self];
284    [[[self pickerController] pickerPopoverController] presentPopoverFromRect:CGRectMake(x, y, width, height)
285                                                                 inView:[self.webView superview]
286                                               permittedArrowDirections:arrowDirection
287                                                               animated:YES];
288}
289
290- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
291{
292    if([navigationController isKindOfClass:[UIImagePickerController class]]){
293        UIImagePickerController* cameraPicker = (UIImagePickerController*)navigationController;
294        
295        if(![cameraPicker.mediaTypes containsObject:(NSString*)kUTTypeImage]){
296            [viewController.navigationItem setTitle:NSLocalizedString(@"Videos", nil)];
297        }
298    }
299}
300
301- (void)cleanup:(CDVInvokedUrlCommand*)command
302{
303    // empty the tmp directory
304    NSFileManager* fileMgr = [[NSFileManager alloc] init];
305    NSError* err = nil;
306    BOOL hasErrors = NO;
307
308    // clear contents of NSTemporaryDirectory
309    NSString* tempDirectoryPath = NSTemporaryDirectory();
310    NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath];
311    NSString* fileName = nil;
312    BOOL result;
313
314    while ((fileName = [directoryEnumerator nextObject])) {
315        // only delete the files we created
316        if (![fileName hasPrefix:CDV_PHOTO_PREFIX]) {
317            continue;
318        }
319        NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName];
320        result = [fileMgr removeItemAtPath:filePath error:&err];
321        if (!result && err) {
322            NSLog(@"Failed to delete: %@ (error: %@)", filePath, err);
323            hasErrors = YES;
324        }
325    }
326
327    CDVPluginResult* pluginResult;
328    if (hasErrors) {
329        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:@"One or more files failed to be deleted."];
330    } else {
331        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
332    }
333    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
334}
335
336- (void)popoverControllerDidDismissPopover:(id)popoverController
337{
338    UIPopoverController* pc = (UIPopoverController*)popoverController;
339
340    [pc dismissPopoverAnimated:YES];
341    pc.delegate = nil;
342    if (self.pickerController && self.pickerController.callbackId && self.pickerController.pickerPopoverController) {
343        self.pickerController.pickerPopoverController = nil;
344        NSString* callbackId = self.pickerController.callbackId;
345        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"];   // error callback expects string ATM
346        [self.commandDelegate sendPluginResult:result callbackId:callbackId];
347    }
348    self.hasPendingOperation = NO;
349}
350
351- (NSData*)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options
352{
353    NSData* data = nil;
354    
355    switch (options.encodingType) {
356        case EncodingTypePNG:
357            data = UIImagePNGRepresentation(image);
358            break;
359        case EncodingTypeJPEG:
360        {
361            if ((options.allowsEditing == NO) && (options.targetSize.width <= 0) && (options.targetSize.height <= 0) && (options.correctOrientation == NO) && (([options.quality integerValue] == 100) || (options.sourceType != UIImagePickerControllerSourceTypeCamera))){
362                // use image unedited as requested , don't resize
363                data = UIImageJPEGRepresentation(image, 1.0);
364            } else {
365                data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
366            }
367            
368            if (options.usesGeolocation) {
369                NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
370                if (controllerMetadata) {
371                    self.data = data;
372                    self.metadata = [[NSMutableDictionary alloc] init];
373                    
374                    NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
375                    if (EXIFDictionary)	{
376                        [self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
377                    }
378                    
379                    if (IsAtLeastiOSVersion(@"8.0")) {
380                        [[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
381                    }
382                    [[self locationManager] startUpdatingLocation];
383                }
384            }
385        }
386            break;
387        default:
388            break;
389    };
390    
391    return data;
392}
393
394- (NSString*)tempFilePath:(NSString*)extension
395{
396    NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
397    NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by Apple (vs [NSFileManager defaultManager]) to be threadsafe
398    NSString* filePath;
399    
400    // generate unique file name
401    int i = 1;
402    do {
403        filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, extension];
404    } while ([fileMgr fileExistsAtPath:filePath]);
405    
406    return filePath;
407}
408
409- (UIImage*)retrieveImage:(NSDictionary*)info options:(CDVPictureOptions*)options
410{
411    // get the image
412    UIImage* image = nil;
413    if (options.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) {
414        image = [info objectForKey:UIImagePickerControllerEditedImage];
415    } else {
416        image = [info objectForKey:UIImagePickerControllerOriginalImage];
417    }
418    
419    if (options.correctOrientation) {
420        image = [image imageCorrectedForCaptureOrientation];
421    }
422    
423    UIImage* scaledImage = nil;
424    
425    if ((options.targetSize.width > 0) && (options.targetSize.height > 0)) {
426        // if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
427        if (options.cropToSize) {
428            scaledImage = [image imageByScalingAndCroppingForSize:options.targetSize];
429        } else {
430            scaledImage = [image imageByScalingNotCroppingForSize:options.targetSize];
431        }
432    }
433    
434    return (scaledImage == nil ? image : scaledImage);
435}
436
437- (void)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info completion:(void (^)(CDVPluginResult* res))completion
438{
439    CDVPluginResult* result = nil;
440    BOOL saveToPhotoAlbum = options.saveToPhotoAlbum;
441    UIImage* image = nil;
442
443    switch (options.destinationType) {
444        case DestinationTypeNativeUri:
445        {
446            NSURL* url = [info objectForKey:UIImagePickerControllerReferenceURL];
447            saveToPhotoAlbum = NO;
448            // If, for example, we use sourceType = Camera, URL might be nil because image is stored in memory.
449            // In this case we must save image to device before obtaining an URI.
450            if (url == nil) {
451                image = [self retrieveImage:info options:options];
452                ALAssetsLibrary* library = [ALAssetsLibrary new];
453                [library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:^(NSURL *assetURL, NSError *error) {
454                    CDVPluginResult* resultToReturn = nil;
455                    if (error) {
456                        resultToReturn = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
457                    } else {
458                        NSString* nativeUri = [[self urlTransformer:assetURL] absoluteString];
459                        resultToReturn = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
460                    }
461                    completion(resultToReturn);
462                }];
463                return;
464            } else {
465                NSString* nativeUri = [[self urlTransformer:url] absoluteString];
466                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
467            }
468        }
469            break;
470        case DestinationTypeFileUri:
471        {
472            image = [self retrieveImage:info options:options];
473            NSData* data = [self processImage:image info:info options:options];
474            if (data) {
475                
476                NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
477                NSString* filePath = [self tempFilePath:extension];
478                NSError* err = nil;
479                
480                // save file
481                if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
482                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
483                } else {
484                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
485                }
486            }
487        }
488            break;
489        case DestinationTypeDataUrl:
490        {
491            image = [self retrieveImage:info options:options];
492            NSData* data = [self processImage:image info:info options:options];
493            if (data)  {
494                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(data)];
495            }
496        }
497            break;
498        default:
499            break;
500    };
501    
502    if (saveToPhotoAlbum && image) {
503        ALAssetsLibrary* library = [ALAssetsLibrary new];
504        [library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:nil];
505    }
506
507    completion(result);
508}
509
510- (CDVPluginResult*)resultForVideo:(NSDictionary*)info
511{
512    NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString];
513    return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath];
514}
515
516- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
517{
518    __weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
519    __weak CDVCamera* weakSelf = self;
520    
521    dispatch_block_t invoke = ^(void) {
522        __block CDVPluginResult* result = nil;
523        
524        NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
525        if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
526            [weakSelf resultForImage:cameraPicker.pictureOptions info:info completion:^(CDVPluginResult* res) {
527                if (![self usesGeolocation] || picker.sourceType != UIImagePickerControllerSourceTypeCamera) {
528                    [weakSelf.commandDelegate sendPluginResult:res callbackId:cameraPicker.callbackId];
529                    weakSelf.hasPendingOperation = NO;
530                    weakSelf.pickerController = nil;
531                }
532            }];
533        }
534        else {
535            result = [weakSelf resultForVideo:info];
536            [weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
537            weakSelf.hasPendingOperation = NO;
538            weakSelf.pickerController = nil;
539        }
540    };
541    
542    if (cameraPicker.pictureOptions.popoverSupported && (cameraPicker.pickerPopoverController != nil)) {
543        [cameraPicker.pickerPopoverController dismissPopoverAnimated:YES];
544        cameraPicker.pickerPopoverController.delegate = nil;
545        cameraPicker.pickerPopoverController = nil;
546        invoke();
547    } else {
548        [[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:invoke];
549    }
550}
551
552// older api calls newer didFinishPickingMediaWithInfo
553- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo
554{
555    NSDictionary* imageInfo = [NSDictionary dictionaryWithObject:image forKey:UIImagePickerControllerOriginalImage];
556
557    [self imagePickerController:picker didFinishPickingMediaWithInfo:imageInfo];
558}
559
560- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker
561{
562    __weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
563    __weak CDVCamera* weakSelf = self;
564    
565    dispatch_block_t invoke = ^ (void) {
566        CDVPluginResult* result;
567        if (picker.sourceType == UIImagePickerControllerSourceTypeCamera && [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != ALAuthorizationStatusAuthorized) {
568            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to camera"];
569        } else if (picker.sourceType != UIImagePickerControllerSourceTypeCamera && [ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusAuthorized) {
570            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"];
571        } else {
572            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"];
573        }
574
575        
576        [weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
577        
578        weakSelf.hasPendingOperation = NO;
579        weakSelf.pickerController = nil;
580    };
581
582    [[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:invoke];
583}
584
585- (CLLocationManager*)locationManager
586{
587	if (locationManager != nil) {
588		return locationManager;
589	}
590    
591	locationManager = [[CLLocationManager alloc] init];
592	[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
593	[locationManager setDelegate:self];
594    
595	return locationManager;
596}
597
598- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation
599{
600    if (locationManager == nil) {
601        return;
602    }
603    
604    [self.locationManager stopUpdatingLocation];
605    self.locationManager = nil;
606    
607    NSMutableDictionary *GPSDictionary = [[NSMutableDictionary dictionary] init];
608    
609    CLLocationDegrees latitude  = newLocation.coordinate.latitude;
610    CLLocationDegrees longitude = newLocation.coordinate.longitude;
611    
612    // latitude
613    if (latitude < 0.0) {
614        latitude = latitude * -1.0f;
615        [GPSDictionary setObject:@"S" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
616    } else {
617        [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
618    }
619    [GPSDictionary setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];
620    
621    // longitude
622    if (longitude < 0.0) {
623        longitude = longitude * -1.0f;
624        [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
625    }
626    else {
627        [GPSDictionary setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
628    }
629    [GPSDictionary setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString*)kCGImagePropertyGPSLongitude];
630    
631    // altitude
632    CGFloat altitude = newLocation.altitude;
633    if (!isnan(altitude)){
634        if (altitude < 0) {
635            altitude = -altitude;
636            [GPSDictionary setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
637        } else {
638            [GPSDictionary setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
639        }
640        [GPSDictionary setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude];
641    }
642    
643    // Time and date
644    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
645    [formatter setDateFormat:@"HH:mm:ss.SSSSSS"];
646    [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
647    [GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp];
648    [formatter setDateFormat:@"yyyy:MM:dd"];
649    [GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp];
650    
651    [self.metadata setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
652    [self imagePickerControllerReturnImageResult];
653}
654
655- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error
656{
657    if (locationManager == nil) {
658        return;
659    }
660
661    [self.locationManager stopUpdatingLocation];
662    self.locationManager = nil;
663    
664    [self imagePickerControllerReturnImageResult];
665}
666
667- (void)imagePickerControllerReturnImageResult
668{
669    CDVPictureOptions* options = self.pickerController.pictureOptions;
670    CDVPluginResult* result = nil;
671    
672    if (self.metadata) {
673        CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL);
674        CFStringRef sourceType = CGImageSourceGetType(sourceImage);
675        
676        CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL);
677        CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
678        CGImageDestinationFinalize(destinationImage);
679        
680        CFRelease(sourceImage);
681        CFRelease(destinationImage);
682    }
683    
684    switch (options.destinationType) {
685        case DestinationTypeFileUri:
686        {
687            NSError* err = nil;
688            NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
689            NSString* filePath = [self tempFilePath:extension];
690            
691            // save file
692            if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) {
693                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
694            }
695            else {
696                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
697            }
698        }
699            break;
700        case DestinationTypeDataUrl:
701        {
702            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(self.data)];
703        }
704            break;
705        case DestinationTypeNativeUri:
706        default:
707            break;
708    };
709    
710    if (result) {
711        [self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId];
712    }
713    
714    self.hasPendingOperation = NO;
715    self.pickerController = nil;
716    self.data = nil;
717    self.metadata = nil;
718    
719    if (options.saveToPhotoAlbum) {
720        ALAssetsLibrary *library = [ALAssetsLibrary new];
721        [library writeImageDataToSavedPhotosAlbum:self.data metadata:self.metadata completionBlock:nil];
722    }
723}
724
725@end
726
727@implementation CDVCameraPicker
728
729- (BOOL)prefersStatusBarHidden
730{
731    return YES;
732}
733
734- (UIViewController*)childViewControllerForStatusBarHidden
735{
736    return nil;
737}
738    
739- (void)viewWillAppear:(BOOL)animated
740{
741    SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
742    if ([self respondsToSelector:sel]) {
743        [self performSelector:sel withObject:nil afterDelay:0];
744    }
745    
746    [super viewWillAppear:animated];
747}
748
749+ (instancetype) createFromPictureOptions:(CDVPictureOptions*)pictureOptions;
750{
751    CDVCameraPicker* cameraPicker = [[CDVCameraPicker alloc] init];
752    cameraPicker.pictureOptions = pictureOptions;
753    cameraPicker.sourceType = pictureOptions.sourceType;
754    cameraPicker.allowsEditing = pictureOptions.allowsEditing;
755    
756    if (cameraPicker.sourceType == UIImagePickerControllerSourceTypeCamera) {
757        // We only allow taking pictures (no video) in this API.
758        cameraPicker.mediaTypes = @[(NSString*)kUTTypeImage];
759        // We can only set the camera device if we're actually using the camera.
760        cameraPicker.cameraDevice = pictureOptions.cameraDirection;
761    } else if (pictureOptions.mediaType == MediaTypeAll) {
762        cameraPicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:cameraPicker.sourceType];
763    } else {
764        NSArray* mediaArray = @[(NSString*)(pictureOptions.mediaType == MediaTypeVideo ? kUTTypeMovie : kUTTypeImage)];
765        cameraPicker.mediaTypes = mediaArray;
766    }
767    
768    return cameraPicker;
769}
770
771@end