diff --git a/src/ios/CDVCamera.m b/src/ios/CDVCamera.m index 2f2738b..6793da4 100644 --- a/src/ios/CDVCamera.m +++ b/src/ios/CDVCamera.m @@ -29,6 +29,7 @@ #import #import #import +#import #ifndef __CORDOVA_4_0_0 #import @@ -159,7 +160,7 @@ static NSString* toBase64(NSData* data) { if (pictureOptions.sourceType == UIImagePickerControllerSourceTypeCamera) { [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { - if(!granted) + if (!granted) { // Denied; show an alert dispatch_async(dispatch_get_main_queue(), ^{ @@ -174,11 +175,32 @@ static NSString* toBase64(NSData* data) { [weakSelf.viewController presentViewController:alertController animated:YES completion:nil]; }); } else { - [weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions]; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions]; + }); } }]; } else { - [weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions]; + [weakSelf options:pictureOptions requestPhotoPermissions:^(BOOL granted) { + if (!granted) { + // Denied; show an alert + dispatch_async(dispatch_get_main_queue(), ^{ + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] message:NSLocalizedString(@"Access to the camera roll has been prohibited; please enable it in the Settings to continue.", nil) preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [weakSelf sendNoPermissionResult:command.callbackId]; + }]]; + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Settings", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; + [weakSelf sendNoPermissionResult:command.callbackId]; + }]]; + [weakSelf.viewController presentViewController:alertController animated:YES completion:nil]; + }); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions]; + }); + } + }]; } }]; } @@ -367,24 +389,51 @@ static NSString* toBase64(NSData* data) { data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f); } - if (options.usesGeolocation) { - NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"]; + if (pickerController.sourceType == UIImagePickerControllerSourceTypeCamera) { + if (options.usesGeolocation) { + NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"]; + if (controllerMetadata) { + self.data = data; + self.metadata = [[NSMutableDictionary alloc] init]; + + NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy]; + if (EXIFDictionary) { + [self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary]; + } + + if (IsAtLeastiOSVersion(@"8.0")) { + [[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0]; + } + [[self locationManager] startUpdatingLocation]; + } + data = nil; + } + } else if (pickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) { + PHAsset* asset = [info objectForKey:@"UIImagePickerControllerPHAsset"]; + NSDictionary* controllerMetadata = [self getImageMetadataFromAsset:asset]; + + self.data = data; if (controllerMetadata) { - self.data = data; self.metadata = [[NSMutableDictionary alloc] init]; NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy]; if (EXIFDictionary) { [self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary]; } - - if (IsAtLeastiOSVersion(@"8.0")) { - [[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0]; + NSMutableDictionary* TIFFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyTIFFDictionary + ]mutableCopy]; + if (TIFFDictionary) { + [self.metadata setObject:TIFFDictionary forKey:(NSString*)kCGImagePropertyTIFFDictionary]; + } + NSMutableDictionary* GPSDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyGPSDictionary +]mutableCopy]; + if (GPSDictionary) { + [self.metadata setObject:GPSDictionary forKey:(NSString*)kCGImagePropertyGPSDictionary +]; } - [[self locationManager] startUpdatingLocation]; } - data = nil; } + } break; default: @@ -394,6 +443,78 @@ static NSString* toBase64(NSData* data) { return data; } +/* -------------------------------------------------------------- +-- get the metadata of the image from a PHAsset +-------------------------------------------------------------- */ +- (NSDictionary*)getImageMetadataFromAsset:(PHAsset*)asset { + + if(asset == nil) { + return nil; + } + + // get photo info from this asset + __block NSDictionary *dict = nil; + PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init]; + imageRequestOptions.synchronous = YES; + [[PHImageManager defaultManager] + requestImageDataForAsset:asset + options:imageRequestOptions + resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { + dict = [self convertImageMetadata:imageData]; // as this imageData is in NSData format so we need a method to convert this NSData into NSDictionary + }]; + return dict; +} + +-(NSDictionary*)convertImageMetadata:(NSData*)imageData { + CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL); + if (imageSource) { + NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]}; + CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options); + if (imageProperties) { + NSDictionary *metadata = (__bridge NSDictionary *)imageProperties; + CFRelease(imageProperties); + CFRelease(imageSource); + NSLog(@"Metadata of selected image%@", metadata);// image metadata after converting NSData into NSDictionary + return metadata; + } + CFRelease(imageSource); + } + + NSLog(@"Can't read image metadata"); + return nil; +} + +- (void)options:(CDVPictureOptions*)options requestPhotoPermissions:(void (^)(BOOL auth))completion +{ + if((unsigned long)options.sourceType == 1){ + completion(YES); + } + else{ + PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; + + switch (status) { + case PHAuthorizationStatusAuthorized: + completion(YES); + break; + case PHAuthorizationStatusNotDetermined: { + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus authorizationStatus) { + if (authorizationStatus == PHAuthorizationStatusAuthorized) { + completion(YES); + } else { + completion(NO); + } + }]; + break; + } + default: + completion(NO); + break; + } + + } + +} + - (NSString*)tempFilePath:(NSString*)extension { NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath]; @@ -454,17 +575,48 @@ static NSString* toBase64(NSData* data) { image = [self retrieveImage:info options:options]; NSData* data = [self processImage:image info:info options:options]; if (data) { + if (pickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) { + NSMutableData *imageDataWithExif = [NSMutableData data]; + if (self.metadata) { + CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL); + CFStringRef sourceType = CGImageSourceGetType(sourceImage); - NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg"; - NSString* filePath = [self tempFilePath:extension]; - NSError* err = nil; + CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL); + CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata); + CGImageDestinationFinalize(destinationImage); - // save file - if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]]; + CFRelease(sourceImage); + CFRelease(destinationImage); + } else { + imageDataWithExif = [self.data mutableCopy]; + } + + NSError* err = nil; + NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg"; + NSString* filePath = [self tempFilePath:extension]; + + // save file + if (![imageDataWithExif writeToFile:filePath options:NSAtomicWrite error:&err]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; + } + else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]]; + } + + } else if (pickerController.sourceType != UIImagePickerControllerSourceTypeCamera || !options.usesGeolocation) { + // No need to save file if usesGeolocation is true since it will be saved after the location is tracked + NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg"; + NSString* filePath = [self tempFilePath:extension]; + NSError* err = nil; + + // save file + if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]]; + } } + } } break; @@ -649,19 +801,23 @@ static NSString* toBase64(NSData* data) { { CDVPictureOptions* options = self.pickerController.pictureOptions; CDVPluginResult* result = nil; + + NSMutableData *imageDataWithExif = [NSMutableData data]; if (self.metadata) { NSData* dataCopy = [self.data mutableCopy]; CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)dataCopy, NULL); CFStringRef sourceType = CGImageSourceGetType(sourceImage); - CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL); + CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL); CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata); CGImageDestinationFinalize(destinationImage); dataCopy = nil; CFRelease(sourceImage); CFRelease(destinationImage); + } else { + imageDataWithExif = [self.data mutableCopy]; } switch (options.destinationType) { @@ -695,7 +851,7 @@ static NSString* toBase64(NSData* data) { self.pickerController = nil; self.data = nil; self.metadata = nil; - + imageDataWithExif = nil; if (options.saveToPhotoAlbum) { UIImageWriteToSavedPhotosAlbum([[UIImage alloc] initWithData:self.data], nil, nil, nil); }