PageRenderTime 188ms CodeModel.GetById 20ms RepoModel.GetById 7ms app.codeStats 0ms

/front/plugins/cordova-plugin-camera/src/ios/CDVCamera.m

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