From 221ad6fcc36d6290aa8875f1b69b6474f898e27b Mon Sep 17 00:00:00 2001 From: Leckie <603418904@qq.com> Date: Fri, 12 Aug 2016 17:56:03 +0800 Subject: [PATCH 1/2] keep the metadata of image, add GPS when take picture from Camera. --- src/ios/CDVCamera.m | 129 +++++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/src/ios/CDVCamera.m b/src/ios/CDVCamera.m index 019141b13..096d467fc 100644 --- a/src/ios/CDVCamera.m +++ b/src/ios/CDVCamera.m @@ -240,13 +240,9 @@ - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)butto - (void)repositionPopover:(CDVInvokedUrlCommand*)command { - if (([[self pickerController] pickerPopoverController] != nil) && [[[self pickerController] pickerPopoverController] isPopoverVisible]) { + NSDictionary* options = [command argumentAtIndex:0 withDefault:nil]; - [[[self pickerController] pickerPopoverController] dismissPopoverAnimated:NO]; - - NSDictionary* options = [command argumentAtIndex:0 withDefault:nil]; - [self displayPopover:options]; - } + [self displayPopover:options]; } - (NSInteger)integerValueForKey:(NSDictionary*)dict key:(NSString*)key defaultValue:(NSInteger)defaultValue @@ -348,47 +344,73 @@ - (void)popoverControllerDidDismissPopover:(id)popoverController self.hasPendingOperation = NO; } -- (NSData*)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options +// Modified by Leckie. 2016-08-12 +- (void)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options completion:(void (^)())completion { - NSData* data = nil; - switch (options.encodingType) { case EncodingTypePNG: - data = UIImagePNGRepresentation(image); + self.data = UIImagePNGRepresentation(image); + completion(); break; case EncodingTypeJPEG: { if ((options.allowsEditing == NO) && (options.targetSize.width <= 0) && (options.targetSize.height <= 0) && (options.correctOrientation == NO) && (([options.quality integerValue] == 100) || (options.sourceType != UIImagePickerControllerSourceTypeCamera))){ // use image unedited as requested , don't resize - data = UIImageJPEGRepresentation(image, 1.0); + self.data = UIImageJPEGRepresentation(image, 1.0); } else { - data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f); + self.data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f); } - if (options.usesGeolocation) { - NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"]; - if (controllerMetadata) { - self.data = data; + // add metedata as much as possible + NSDictionary* controllerMetadata = [info objectForKey:UIImagePickerControllerMediaMetadata]; + if (controllerMetadata) { + /*NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy]; + if (EXIFDictionary) { self.metadata = [[NSMutableDictionary alloc] init]; - - NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy]; - if (EXIFDictionary) { + [self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary]; + }*/ + self.metadata = [controllerMetadata mutableCopy]; + completion(); + } else { + // just try + NSURL *assertURL = [info objectForKey:UIImagePickerControllerReferenceURL]; + ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; + [library assetForURL:assertURL resultBlock:^(ALAsset *asset) { + NSDictionary *metadata = asset.defaultRepresentation.metadata; + NSMutableDictionary *imageMetadata = [[NSMutableDictionary alloc] initWithDictionary:metadata]; + [info setValue:imageMetadata forKey:UIImagePickerControllerMediaMetadata]; + self.metadata = imageMetadata; + /*NSMutableDictionary *EXIFDictionary = [[imageMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy]; + if (EXIFDictionary) { + self.metadata = [[NSMutableDictionary alloc] init]; [self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary]; } - - if (IsAtLeastiOSVersion(@"8.0")) { - [[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0]; - } - [[self locationManager] startUpdatingLocation]; + NSMutableDictionary *GPSDictionary = [[imageMetadata objectForKey:(NSString*)kCGImagePropertyGPSDictionary] mutableCopy]; + if(GPSDictionary) { + if(!self.metadata) { + self.metadata = [[NSMutableDictionary alloc] init]; + } + [self.metadata setObject:GPSDictionary forKey:(NSString*)kCGImagePropertyGPSDictionary]; + }*/ + completion(); + } failureBlock:^(NSError *error) { + completion(); + NSLog(@"error %@", [error description]); + }]; + } + // only when source type is UIImagePickerControllerSourceTypeCamera + if (options.usesGeolocation && options.sourceType == UIImagePickerControllerSourceTypeCamera) { + if (IsAtLeastiOSVersion(@"8.0")) { + [[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0]; } + [[self locationManager] startUpdatingLocation]; } } break; default: + completion(); break; }; - - return data; } - (NSString*)tempFilePath:(NSString*)extension @@ -436,7 +458,6 @@ - (UIImage*)retrieveImage:(NSDictionary*)info options:(CDVPictureOptions*)option - (void)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info completion:(void (^)(CDVPluginResult* res))completion { - CDVPluginResult* result = nil; BOOL saveToPhotoAlbum = options.saveToPhotoAlbum; UIImage* image = nil; @@ -463,36 +484,36 @@ - (void)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info comp return; } else { NSString* nativeUri = [[self urlTransformer:url] absoluteString]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri]; + completion(result); } } break; case DestinationTypeFileUri: { image = [self retrieveImage:info options:options]; - NSData* data = [self processImage:image info:info options:options]; - if (data) { - + [self processImage:image info:info options:options completion:^{ NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg"; NSString* filePath = [self tempFilePath:extension]; NSError* err = nil; - + CDVPluginResult* result = nil; // save file - if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) { + if (![[self imageDataWithMetaData] 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]]; } - } + completion(result); + }]; } break; case DestinationTypeDataUrl: { image = [self retrieveImage:info options:options]; - NSData* data = [self processImage:image info:info options:options]; - if (data) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(data)]; - } + [self processImage:image info:info options:options completion:^{ + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64([self imageDataWithMetaData])]; + completion(result); + }]; } break; default: @@ -503,8 +524,6 @@ - (void)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info comp ALAssetsLibrary* library = [ALAssetsLibrary new]; [library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:nil]; } - - completion(result); } - (CDVPluginResult*)resultForVideo:(NSDictionary*)info @@ -513,6 +532,7 @@ - (CDVPluginResult*)resultForVideo:(NSDictionary*)info return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath]; } +// Modified by Leckie. 2016-08-10 - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info { __weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker; @@ -524,7 +544,8 @@ - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingM NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType]; if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) { [weakSelf resultForImage:cameraPicker.pictureOptions info:info completion:^(CDVPluginResult* res) { - if (![self usesGeolocation] || picker.sourceType != UIImagePickerControllerSourceTypeCamera) { + if(!(self.pickerController.pictureOptions.usesGeolocation && self.pickerController.pictureOptions.sourceType == UIImagePickerControllerSourceTypeCamera)) { + // If usesGeolocation and sourceType=Camera will sendPluginResult later. In imagePickerControllerReturnImageResult. [weakSelf.commandDelegate sendPluginResult:res callbackId:cameraPicker.callbackId]; weakSelf.hasPendingOperation = NO; weakSelf.pickerController = nil; @@ -664,22 +685,28 @@ - (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)e [self imagePickerControllerReturnImageResult]; } -- (void)imagePickerControllerReturnImageResult -{ - CDVPictureOptions* options = self.pickerController.pictureOptions; - CDVPluginResult* result = nil; - +// Added by Leckie. 2016-08-12 +- (NSData*)imageDataWithMetaData { + NSData *data = [self.data mutableCopy]; if (self.metadata) { CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL); CFStringRef sourceType = CGImageSourceGetType(sourceImage); - CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL); + CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)data, sourceType, 1, NULL); CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata); CGImageDestinationFinalize(destinationImage); CFRelease(sourceImage); CFRelease(destinationImage); } + NSLog(@"metadata: %@", self.metadata); + return data; +} + +- (void)imagePickerControllerReturnImageResult +{ + CDVPictureOptions* options = self.pickerController.pictureOptions; + CDVPluginResult* result = nil; switch (options.destinationType) { case DestinationTypeFileUri: @@ -689,7 +716,7 @@ - (void)imagePickerControllerReturnImageResult NSString* filePath = [self tempFilePath:extension]; // save file - if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) { + if (![[self imageDataWithMetaData] writeToFile:filePath options:NSAtomicWrite error:&err]) { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; } else { @@ -699,7 +726,7 @@ - (void)imagePickerControllerReturnImageResult break; case DestinationTypeDataUrl: { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(self.data)]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64([self imageDataWithMetaData])]; } break; case DestinationTypeNativeUri: @@ -716,10 +743,12 @@ - (void)imagePickerControllerReturnImageResult self.data = nil; self.metadata = nil; + // This is no use. In fact. + /* if (options.saveToPhotoAlbum) { ALAssetsLibrary *library = [ALAssetsLibrary new]; [library writeImageDataToSavedPhotosAlbum:self.data metadata:self.metadata completionBlock:nil]; - } + }*/ } @end From 16a4315f75161250eb18635ac9c919f0460704b0 Mon Sep 17 00:00:00 2001 From: Leckie <603418904@qq.com> Date: Mon, 15 Aug 2016 17:31:19 +0800 Subject: [PATCH 2/2] fix: test processImage --- src/ios/CDVCamera.m | 4 ++++ .../CDVCameraLibTests/CameraTest.m | 21 +++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/ios/CDVCamera.m b/src/ios/CDVCamera.m index 096d467fc..2d76429bf 100644 --- a/src/ios/CDVCamera.m +++ b/src/ios/CDVCamera.m @@ -374,6 +374,10 @@ - (void)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictur } else { // just try NSURL *assertURL = [info objectForKey:UIImagePickerControllerReferenceURL]; + if(!assertURL) { + completion(); + break; + } ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; [library assetForURL:assertURL resultBlock:^(ALAsset *asset) { NSDictionary *metadata = asset.defaultRepresentation.metadata; diff --git a/tests/ios/CDVCameraTest/CDVCameraLibTests/CameraTest.m b/tests/ios/CDVCameraTest/CDVCameraLibTests/CameraTest.m index b9439d108..8e032ea3d 100644 --- a/tests/ios/CDVCameraTest/CDVCameraLibTests/CameraTest.m +++ b/tests/ios/CDVCameraTest/CDVCameraLibTests/CameraTest.m @@ -33,7 +33,7 @@ @interface CameraTest : XCTestCase @interface CDVCamera () // expose private interface -- (NSData*)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options; +- (void)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options completion:(void (^)())completion; - (UIImage*)retrieveImage:(NSDictionary*)info options:(CDVPictureOptions*)options; - (CDVPluginResult*)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info; - (CDVPluginResult*)resultForVideo:(NSDictionary*)info; @@ -464,7 +464,6 @@ - (void) testRetrieveImage - (void) testProcessImage { CDVPictureOptions* pictureOptions = [[CDVPictureOptions alloc] init]; - NSData* resultData; UIImage* originalImage = [self createImage:CGRectMake(0, 0, 1024, 768) orientation:UIImageOrientationDown]; NSData* originalImageDataPNG = UIImagePNGRepresentation(originalImage); @@ -478,9 +477,10 @@ - (void) testProcessImage pictureOptions.correctOrientation = NO; pictureOptions.encodingType = EncodingTypePNG; - resultData = [self.plugin processImage:originalImage info:@{} options:pictureOptions]; - XCTAssertEqualObjects([resultData base64EncodedStringWithOptions:0], [originalImageDataPNG base64EncodedStringWithOptions:0]); - + [self.plugin processImage:originalImage info:@{} options:pictureOptions completion:^{ + XCTAssertEqualObjects([self.plugin.data base64EncodedStringWithOptions:0], [originalImageDataPNG base64EncodedStringWithOptions:0]); + }]; + // Original, JPEG, full quality pictureOptions.allowsEditing = NO; @@ -489,8 +489,10 @@ - (void) testProcessImage pictureOptions.correctOrientation = NO; pictureOptions.encodingType = EncodingTypeJPEG; - resultData = [self.plugin processImage:originalImage info:@{} options:pictureOptions]; - XCTAssertEqualObjects([resultData base64EncodedStringWithOptions:0], [originalImageDataJPEG base64EncodedStringWithOptions:0]); + [self.plugin processImage:originalImage info:@{} options:pictureOptions completion:^{ + XCTAssertEqualObjects([self.plugin.data base64EncodedStringWithOptions:0], [originalImageDataJPEG base64EncodedStringWithOptions:0]); + }]; + // Original, JPEG, with quality value @@ -502,8 +504,9 @@ - (void) testProcessImage pictureOptions.quality = @(57); NSData* originalImageDataJPEGWithQuality = UIImageJPEGRepresentation(originalImage, [pictureOptions.quality floatValue]/ 100.f); - resultData = [self.plugin processImage:originalImage info:@{} options:pictureOptions]; - XCTAssertEqualObjects([resultData base64EncodedStringWithOptions:0], [originalImageDataJPEGWithQuality base64EncodedStringWithOptions:0]); + [self.plugin processImage:originalImage info:@{} options:pictureOptions completion:^{ + XCTAssertEqualObjects([self.plugin.data base64EncodedStringWithOptions:0], [originalImageDataJPEGWithQuality base64EncodedStringWithOptions:0]); + }]; // TODO: usesGeolocation is not tested }