mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-25 07:48:08 +01:00
620 lines
25 KiB
Diff
620 lines
25 KiB
Diff
|
--- ImagePickerManager.m 1985-10-26 04:15:00.000000000 -0400
|
||
|
+++ ImagePickerManager.m 2020-11-15 21:01:40.000000000 -0500
|
||
|
@@ -1,9 +1,11 @@
|
||
|
#import "ImagePickerManager.h"
|
||
|
#import <React/RCTConvert.h>
|
||
|
-#import <AssetsLibrary/AssetsLibrary.h>
|
||
|
#import <AVFoundation/AVFoundation.h>
|
||
|
#import <Photos/Photos.h>
|
||
|
#import <React/RCTUtils.h>
|
||
|
+#if !TARGET_OS_MACCATALYST
|
||
|
+#import <AssetsLibrary/AssetsLibrary.h>
|
||
|
+#endif
|
||
|
|
||
|
@import MobileCoreServices;
|
||
|
|
||
|
@@ -42,25 +44,25 @@
|
||
|
{
|
||
|
self.callback = callback; // Save the callback so we can use it from the delegate methods
|
||
|
self.options = options;
|
||
|
-
|
||
|
+
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
-
|
||
|
+
|
||
|
NSString *title = [self.options valueForKey:@"title"];
|
||
|
if ([title isEqual:[NSNull null]] || title.length == 0) {
|
||
|
title = nil; // A more visually appealing UIAlertControl is displayed with a nil title rather than title = @""
|
||
|
}
|
||
|
NSString *cancelTitle = [self.options valueForKey:@"cancelButtonTitle"];
|
||
|
NSString *takePhotoButtonTitle = [self.options valueForKey:@"takePhotoButtonTitle"];
|
||
|
- NSString *chooseFromLibraryButtonTitle = [self.options valueForKey:@"chooseFromLibraryButtonTitle"];
|
||
|
-
|
||
|
+ NSString *chooseFromLibraryButtonTitle = [self.options valueForKey:@"chooseFromLibraryButtonTitle"];
|
||
|
+
|
||
|
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
|
||
|
alertController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]];
|
||
|
-
|
||
|
+
|
||
|
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
|
||
|
self.callback(@[@{@"didCancel": @YES}]); // Return callback for 'cancel' action (if is required)
|
||
|
}];
|
||
|
[alertController addAction:cancelAction];
|
||
|
-
|
||
|
+
|
||
|
if (![takePhotoButtonTitle isEqual:[NSNull null]] && takePhotoButtonTitle.length > 0) {
|
||
|
UIAlertAction *takePhotoAction = [UIAlertAction actionWithTitle:takePhotoButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
||
|
[self actionHandler:action];
|
||
|
@@ -73,7 +75,7 @@
|
||
|
}];
|
||
|
[alertController addAction:chooseFromLibraryAction];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
// Add custom buttons to action sheet
|
||
|
if ([self.options objectForKey:@"customButtons"] && [[self.options objectForKey:@"customButtons"] isKindOfClass:[NSArray class]]) {
|
||
|
self.customButtons = [self.options objectForKey:@"customButtons"];
|
||
|
@@ -85,16 +87,16 @@
|
||
|
[alertController addAction:customAction];
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
UIViewController *root = RCTPresentedViewController();
|
||
|
-
|
||
|
+
|
||
|
/* On iPad, UIAlertController presents a popover view rather than an action sheet like on iPhone. We must provide the location
|
||
|
- of the location to show the popover in this case. For simplicity, we'll just display it on the bottom center of the screen
|
||
|
- to mimic an action sheet */
|
||
|
+ of the location to show the popover in this case. For simplicity, we'll just display it on the bottom center of the screen
|
||
|
+ to mimic an action sheet */
|
||
|
alertController.popoverPresentationController.sourceView = root.view;
|
||
|
alertController.popoverPresentationController.sourceRect = CGRectMake(root.view.bounds.size.width / 2.0, root.view.bounds.size.height, 1.0, 1.0);
|
||
|
-
|
||
|
- if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||
|
+
|
||
|
+ if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
|
||
|
alertController.popoverPresentationController.permittedArrowDirections = 0;
|
||
|
for (id subview in alertController.view.subviews) {
|
||
|
if ([subview isMemberOfClass:[UIView class]]) {
|
||
|
@@ -102,7 +104,7 @@
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
[root presentViewController:alertController animated:YES completion:nil];
|
||
|
});
|
||
|
}
|
||
|
@@ -119,7 +121,7 @@
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
if ([action.title isEqualToString:[self.options valueForKey:@"takePhotoButtonTitle"]]) { // Take photo
|
||
|
[self launchImagePicker:RNImagePickerTargetCamera];
|
||
|
}
|
||
|
@@ -137,7 +139,7 @@
|
||
|
- (void)launchImagePicker:(RNImagePickerTarget)target
|
||
|
{
|
||
|
self.picker = [[UIImagePickerController alloc] init];
|
||
|
-
|
||
|
+
|
||
|
if (target == RNImagePickerTargetCamera) {
|
||
|
#if TARGET_IPHONE_SIMULATOR
|
||
|
self.callback(@[@{@"error": @"Camera not available on simulator"}]);
|
||
|
@@ -155,10 +157,10 @@
|
||
|
else { // RNImagePickerTargetLibrarySingleImage
|
||
|
self.picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
if ([[self.options objectForKey:@"mediaType"] isEqualToString:@"video"]
|
||
|
|| [[self.options objectForKey:@"mediaType"] isEqualToString:@"mixed"]) {
|
||
|
-
|
||
|
+
|
||
|
if ([[self.options objectForKey:@"videoQuality"] isEqualToString:@"high"]) {
|
||
|
self.picker.videoQuality = UIImagePickerControllerQualityTypeHigh;
|
||
|
}
|
||
|
@@ -168,7 +170,7 @@
|
||
|
else {
|
||
|
self.picker.videoQuality = UIImagePickerControllerQualityTypeMedium;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
id durationLimit = [self.options objectForKey:@"durationLimit"];
|
||
|
if (durationLimit) {
|
||
|
self.picker.videoMaximumDuration = [durationLimit doubleValue];
|
||
|
@@ -182,13 +184,13 @@
|
||
|
} else {
|
||
|
self.picker.mediaTypes = @[(NSString *)kUTTypeImage];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
if ([[self.options objectForKey:@"allowsEditing"] boolValue]) {
|
||
|
self.picker.allowsEditing = true;
|
||
|
}
|
||
|
self.picker.modalPresentationStyle = UIModalPresentationCurrentContext;
|
||
|
self.picker.delegate = self;
|
||
|
-
|
||
|
+
|
||
|
// Check permissions
|
||
|
void (^showPickerViewController)() = ^void() {
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
@@ -196,57 +198,57 @@
|
||
|
[root presentViewController:self.picker animated:YES completion:nil];
|
||
|
});
|
||
|
};
|
||
|
-
|
||
|
+
|
||
|
if (target == RNImagePickerTargetCamera) {
|
||
|
[self checkCameraPermissions:^(BOOL granted) {
|
||
|
if (!granted) {
|
||
|
self.callback(@[@{@"error": @"Camera permissions not granted"}]);
|
||
|
return;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
showPickerViewController();
|
||
|
}];
|
||
|
}
|
||
|
else { // RNImagePickerTargetLibrarySingleImage
|
||
|
- if (@available(iOS 11.0, *)) {
|
||
|
- showPickerViewController();
|
||
|
- } else {
|
||
|
- [self checkPhotosPermissions:^(BOOL granted) {
|
||
|
- if (!granted) {
|
||
|
- self.callback(@[@{@"error": @"Photo library permissions not granted"}]);
|
||
|
- return;
|
||
|
- }
|
||
|
-
|
||
|
- showPickerViewController();
|
||
|
- }];
|
||
|
- }
|
||
|
+ if (@available(iOS 11.0, *)) {
|
||
|
+ showPickerViewController();
|
||
|
+ } else {
|
||
|
+ [self checkPhotosPermissions:^(BOOL granted) {
|
||
|
+ if (!granted) {
|
||
|
+ self.callback(@[@{@"error": @"Photo library permissions not granted"}]);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ showPickerViewController();
|
||
|
+ }];
|
||
|
+ }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (NSString * _Nullable)originalFilenameForAsset:(PHAsset * _Nullable)asset assetType:(PHAssetResourceType)type {
|
||
|
if (!asset) { return nil; }
|
||
|
-
|
||
|
+
|
||
|
PHAssetResource *originalResource;
|
||
|
// Get the underlying resources for the PHAsset (PhotoKit)
|
||
|
NSArray<PHAssetResource *> *pickedAssetResources = [PHAssetResource assetResourcesForAsset:asset];
|
||
|
-
|
||
|
+
|
||
|
// Find the original resource (underlying image) for the asset, which has the desired filename
|
||
|
for (PHAssetResource *resource in pickedAssetResources) {
|
||
|
if (resource.type == type) {
|
||
|
originalResource = resource;
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
return originalResource.originalFilename;
|
||
|
}
|
||
|
|
||
|
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
|
||
|
{
|
||
|
dispatch_block_t dismissCompletionBlock = ^{
|
||
|
-
|
||
|
- NSURL *imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
|
||
|
+
|
||
|
+ NSURL *imageURL = [info valueForKey:UIImagePickerControllerPHAsset];
|
||
|
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
|
||
|
-
|
||
|
+
|
||
|
NSString *fileName;
|
||
|
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
|
||
|
NSString *tempFileName = [[NSUUID UUID] UUIDString];
|
||
|
@@ -264,18 +266,18 @@
|
||
|
NSURL *videoURL = info[UIImagePickerControllerMediaURL];
|
||
|
fileName = videoURL.lastPathComponent;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
// We default to path to the temporary directory
|
||
|
NSString *path = [[NSTemporaryDirectory()stringByStandardizingPath] stringByAppendingPathComponent:fileName];
|
||
|
-
|
||
|
+
|
||
|
// If storage options are provided, we use the documents directory which is persisted
|
||
|
if ([self.options objectForKey:@"storageOptions"] && [[self.options objectForKey:@"storageOptions"] isKindOfClass:[NSDictionary class]]) {
|
||
|
NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
|
||
|
-
|
||
|
+
|
||
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||
|
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||
|
path = [documentsDirectory stringByAppendingPathComponent:fileName];
|
||
|
-
|
||
|
+
|
||
|
// Creates documents subdirectory, if provided
|
||
|
if ([storageOptions objectForKey:@"path"]) {
|
||
|
NSString *newPath = [documentsDirectory stringByAppendingPathComponent:[storageOptions objectForKey:@"path"]];
|
||
|
@@ -291,10 +293,10 @@
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
// Create the response object
|
||
|
self.response = [[NSMutableDictionary alloc] init];
|
||
|
-
|
||
|
+
|
||
|
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) { // PHOTOS
|
||
|
UIImage *originalImage;
|
||
|
if ([[self.options objectForKey:@"allowsEditing"] boolValue]) {
|
||
|
@@ -303,13 +305,15 @@
|
||
|
else {
|
||
|
originalImage = [info objectForKey:UIImagePickerControllerOriginalImage];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
if (imageURL) {
|
||
|
PHAsset *pickedAsset;
|
||
|
if (@available(iOS 11.0, *)) {
|
||
|
- pickedAsset = [info objectForKey: UIImagePickerControllerPHAsset];
|
||
|
+ pickedAsset = [info objectForKey: UIImagePickerControllerPHAsset];
|
||
|
} else {
|
||
|
- pickedAsset = [PHAsset fetchAssetsWithALAssetURLs:@[imageURL] options:nil].lastObject;
|
||
|
+#if !TARGET_OS_MACCATALYST
|
||
|
+ pickedAsset = [PHAsset fetchAssetsWithALAssetURLs:@[imageURL] options:nil].lastObject;
|
||
|
+#endif
|
||
|
}
|
||
|
|
||
|
NSString *originalFilename = [self originalFilenameForAsset:pickedAsset assetType:PHAssetResourceTypePhoto];
|
||
|
@@ -322,9 +326,10 @@
|
||
|
self.response[@"timestamp"] = [[ImagePickerManager ISO8601DateFormatter] stringFromDate:pickedAsset.creationDate];
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
// GIFs break when resized, so we handle them differently
|
||
|
if (imageURL && [[imageURL absoluteString] rangeOfString:@"ext=GIF"].location != NSNotFound) {
|
||
|
+#if !TARGET_OS_MACCATALYST
|
||
|
ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
|
||
|
[assetsLibrary assetForURL:imageURL resultBlock:^(ALAsset *asset) {
|
||
|
ALAssetRepresentation *rep = [asset defaultRepresentation];
|
||
|
@@ -333,38 +338,39 @@
|
||
|
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:repSize error:nil];
|
||
|
NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
|
||
|
[data writeToFile:path atomically:YES];
|
||
|
-
|
||
|
+
|
||
|
NSMutableDictionary *gifResponse = [[NSMutableDictionary alloc] init];
|
||
|
[gifResponse setObject:@(originalImage.size.width) forKey:@"width"];
|
||
|
[gifResponse setObject:@(originalImage.size.height) forKey:@"height"];
|
||
|
-
|
||
|
+
|
||
|
BOOL vertical = (originalImage.size.width < originalImage.size.height) ? YES : NO;
|
||
|
[gifResponse setObject:@(vertical) forKey:@"isVertical"];
|
||
|
-
|
||
|
+
|
||
|
if (![[self.options objectForKey:@"noData"] boolValue]) {
|
||
|
NSString *dataString = [data base64EncodedStringWithOptions:0];
|
||
|
[gifResponse setObject:dataString forKey:@"data"];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
NSURL *fileURL = [NSURL fileURLWithPath:path];
|
||
|
[gifResponse setObject:[fileURL absoluteString] forKey:@"uri"];
|
||
|
-
|
||
|
+
|
||
|
NSNumber *fileSizeValue = nil;
|
||
|
NSError *fileSizeError = nil;
|
||
|
[fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:&fileSizeError];
|
||
|
if (fileSizeValue){
|
||
|
[gifResponse setObject:fileSizeValue forKey:@"fileSize"];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
self.callback(@[gifResponse]);
|
||
|
} failureBlock:^(NSError *error) {
|
||
|
self.callback(@[@{@"error": error.localizedFailureReason}]);
|
||
|
}];
|
||
|
+#endif
|
||
|
return;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
UIImage *editedImage = [self fixOrientation:originalImage]; // Rotate the image for upload to web
|
||
|
-
|
||
|
+
|
||
|
// If needed, downscale image
|
||
|
float maxWidth = editedImage.size.width;
|
||
|
float maxHeight = editedImage.size.height;
|
||
|
@@ -375,7 +381,7 @@
|
||
|
maxHeight = [[self.options valueForKey:@"maxHeight"] floatValue];
|
||
|
}
|
||
|
editedImage = [self downscaleImageIfNecessary:editedImage maxWidth:maxWidth maxHeight:maxHeight];
|
||
|
-
|
||
|
+
|
||
|
NSData *data;
|
||
|
NSString *mimeType;
|
||
|
if ([[self.options objectForKey:@"imageFileType"] isEqualToString:@"png"]) {
|
||
|
@@ -388,36 +394,37 @@
|
||
|
}
|
||
|
[self.response setObject:mimeType forKey:@"type"];
|
||
|
[data writeToFile:path atomically:YES];
|
||
|
-
|
||
|
+
|
||
|
if (![[self.options objectForKey:@"noData"] boolValue]) {
|
||
|
NSString *dataString = [data base64EncodedStringWithOptions:0]; // base64 encoded image string
|
||
|
[self.response setObject:dataString forKey:@"data"];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
BOOL vertical = (editedImage.size.width < editedImage.size.height) ? YES : NO;
|
||
|
[self.response setObject:@(vertical) forKey:@"isVertical"];
|
||
|
NSURL *fileURL = [NSURL fileURLWithPath:path];
|
||
|
NSString *filePath = [fileURL absoluteString];
|
||
|
[self.response setObject:filePath forKey:@"uri"];
|
||
|
-
|
||
|
+
|
||
|
// add ref to the original image
|
||
|
NSString *origURL = [imageURL absoluteString];
|
||
|
if (origURL) {
|
||
|
- [self.response setObject:origURL forKey:@"origURL"];
|
||
|
+ [self.response setObject:origURL forKey:@"origURL"];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
NSNumber *fileSizeValue = nil;
|
||
|
NSError *fileSizeError = nil;
|
||
|
[fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:&fileSizeError];
|
||
|
if (fileSizeValue){
|
||
|
[self.response setObject:fileSizeValue forKey:@"fileSize"];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
[self.response setObject:@(editedImage.size.width) forKey:@"width"];
|
||
|
[self.response setObject:@(editedImage.size.height) forKey:@"height"];
|
||
|
-
|
||
|
+
|
||
|
NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
|
||
|
if (storageOptions && [[storageOptions objectForKey:@"cameraRoll"] boolValue] == YES && self.picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||
|
+#if !TARGET_OS_MACCATALYST
|
||
|
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
|
||
|
if ([[storageOptions objectForKey:@"waitUntilSaved"] boolValue]) {
|
||
|
[library writeImageToSavedPhotosAlbum:originalImage.CGImage metadata:[info valueForKey:UIImagePickerControllerMediaMetadata] completionBlock:^(NSURL *assetURL, NSError *error) {
|
||
|
@@ -440,13 +447,15 @@
|
||
|
} else {
|
||
|
[library writeImageToSavedPhotosAlbum:originalImage.CGImage metadata:[info valueForKey:UIImagePickerControllerMediaMetadata] completionBlock:nil];
|
||
|
}
|
||
|
+#endif
|
||
|
}
|
||
|
}
|
||
|
else { // VIDEO
|
||
|
- NSURL *videoRefURL = info[UIImagePickerControllerReferenceURL];
|
||
|
+ NSURL *videoRefURL = info[UIImagePickerControllerPHAsset];
|
||
|
NSURL *videoURL = info[UIImagePickerControllerMediaURL];
|
||
|
NSURL *videoDestinationURL = [NSURL fileURLWithPath:path];
|
||
|
-
|
||
|
+
|
||
|
+#if !TARGET_OS_MACCATALYST
|
||
|
if (videoRefURL) {
|
||
|
PHAsset *pickedAsset = [PHAsset fetchAssetsWithALAssetURLs:@[videoRefURL] options:nil].lastObject;
|
||
|
NSString *originalFilename = [self originalFilenameForAsset:pickedAsset assetType:PHAssetResourceTypeVideo];
|
||
|
@@ -459,39 +468,41 @@
|
||
|
self.response[@"timestamp"] = [[ImagePickerManager ISO8601DateFormatter] stringFromDate:pickedAsset.creationDate];
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+#endif
|
||
|
+
|
||
|
if ([videoURL.URLByResolvingSymlinksInPath.path isEqualToString:videoDestinationURL.URLByResolvingSymlinksInPath.path] == NO) {
|
||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||
|
-
|
||
|
+
|
||
|
// Delete file if it already exists
|
||
|
if ([fileManager fileExistsAtPath:videoDestinationURL.path]) {
|
||
|
[fileManager removeItemAtURL:videoDestinationURL error:nil];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
if (videoURL) { // Protect against reported crash
|
||
|
- NSError *error = nil;
|
||
|
-
|
||
|
- // If we have write access to the source file, move it. Otherwise use copy.
|
||
|
- if ([fileManager isWritableFileAtPath:[videoURL path]]) {
|
||
|
- [fileManager moveItemAtURL:videoURL toURL:videoDestinationURL error:&error];
|
||
|
- } else {
|
||
|
- [fileManager copyItemAtURL:videoURL toURL:videoDestinationURL error:&error];
|
||
|
- }
|
||
|
-
|
||
|
- if (error) {
|
||
|
- self.callback(@[@{@"error": error.localizedFailureReason}]);
|
||
|
- return;
|
||
|
- }
|
||
|
+ NSError *error = nil;
|
||
|
+
|
||
|
+ // If we have write access to the source file, move it. Otherwise use copy.
|
||
|
+ if ([fileManager isWritableFileAtPath:[videoURL path]]) {
|
||
|
+ [fileManager moveItemAtURL:videoURL toURL:videoDestinationURL error:&error];
|
||
|
+ } else {
|
||
|
+ [fileManager copyItemAtURL:videoURL toURL:videoDestinationURL error:&error];
|
||
|
+ }
|
||
|
+
|
||
|
+ if (error) {
|
||
|
+ self.callback(@[@{@"error": error.localizedFailureReason}]);
|
||
|
+ return;
|
||
|
+ }
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
[self.response setObject:videoDestinationURL.absoluteString forKey:@"uri"];
|
||
|
if (videoRefURL.absoluteString) {
|
||
|
[self.response setObject:videoRefURL.absoluteString forKey:@"origURL"];
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
|
||
|
if (storageOptions && [[storageOptions objectForKey:@"cameraRoll"] boolValue] == YES && self.picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||
|
+#if !TARGET_OS_MACCATALYST
|
||
|
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
|
||
|
[library writeVideoAtPathToSavedPhotosAlbum:videoDestinationURL completionBlock:^(NSURL *assetURL, NSError *error) {
|
||
|
if (error) {
|
||
|
@@ -509,22 +520,23 @@
|
||
|
self.response[@"timestamp"] = [[ImagePickerManager ISO8601DateFormatter] stringFromDate:capturedAsset.creationDate];
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
self.callback(@[self.response]);
|
||
|
}
|
||
|
}
|
||
|
}];
|
||
|
+#endif
|
||
|
}
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
// If storage options are provided, check the skipBackup flag
|
||
|
if ([self.options objectForKey:@"storageOptions"] && [[self.options objectForKey:@"storageOptions"] isKindOfClass:[NSDictionary class]]) {
|
||
|
NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
|
||
|
-
|
||
|
+
|
||
|
if ([[storageOptions objectForKey:@"skipBackup"] boolValue]) {
|
||
|
[self addSkipBackupAttributeToItemAtPath:path]; // Don't back up the file to iCloud
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
if ([[storageOptions objectForKey:@"waitUntilSaved"] boolValue] == NO ||
|
||
|
[[storageOptions objectForKey:@"cameraRoll"] boolValue] == NO ||
|
||
|
self.picker.sourceType != UIImagePickerControllerSourceTypeCamera)
|
||
|
@@ -536,7 +548,7 @@
|
||
|
self.callback(@[self.response]);
|
||
|
}
|
||
|
};
|
||
|
-
|
||
|
+
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
[picker dismissViewControllerAnimated:YES completion:dismissCompletionBlock];
|
||
|
});
|
||
|
@@ -595,12 +607,12 @@
|
||
|
- (UIImage*)downscaleImageIfNecessary:(UIImage*)image maxWidth:(float)maxWidth maxHeight:(float)maxHeight
|
||
|
{
|
||
|
UIImage* newImage = image;
|
||
|
-
|
||
|
+
|
||
|
// Nothing to do here
|
||
|
if (image.size.width <= maxWidth && image.size.height <= maxHeight) {
|
||
|
return newImage;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
CGSize scaledSize = CGSizeMake(image.size.width, image.size.height);
|
||
|
if (maxWidth < scaledSize.width) {
|
||
|
scaledSize = CGSizeMake(maxWidth, (maxWidth / scaledSize.width) * scaledSize.height);
|
||
|
@@ -608,11 +620,11 @@
|
||
|
if (maxHeight < scaledSize.height) {
|
||
|
scaledSize = CGSizeMake((maxHeight / scaledSize.height) * scaledSize.width, maxHeight);
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
// If the pixels are floats, it causes a white line in iOS8 and probably other versions too
|
||
|
scaledSize.width = (int)scaledSize.width;
|
||
|
scaledSize.height = (int)scaledSize.height;
|
||
|
-
|
||
|
+
|
||
|
UIGraphicsBeginImageContext(scaledSize); // this will resize
|
||
|
[image drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
|
||
|
newImage = UIGraphicsGetImageFromCurrentImageContext();
|
||
|
@@ -620,7 +632,7 @@
|
||
|
NSLog(@"could not scale image");
|
||
|
}
|
||
|
UIGraphicsEndImageContext();
|
||
|
-
|
||
|
+
|
||
|
return newImage;
|
||
|
}
|
||
|
|
||
|
@@ -628,7 +640,7 @@
|
||
|
if (srcImg.imageOrientation == UIImageOrientationUp) {
|
||
|
return srcImg;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
CGAffineTransform transform = CGAffineTransformIdentity;
|
||
|
switch (srcImg.imageOrientation) {
|
||
|
case UIImageOrientationDown:
|
||
|
@@ -636,13 +648,13 @@
|
||
|
transform = CGAffineTransformTranslate(transform, srcImg.size.width, srcImg.size.height);
|
||
|
transform = CGAffineTransformRotate(transform, M_PI);
|
||
|
break;
|
||
|
-
|
||
|
+
|
||
|
case UIImageOrientationLeft:
|
||
|
case UIImageOrientationLeftMirrored:
|
||
|
transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
|
||
|
transform = CGAffineTransformRotate(transform, M_PI_2);
|
||
|
break;
|
||
|
-
|
||
|
+
|
||
|
case UIImageOrientationRight:
|
||
|
case UIImageOrientationRightMirrored:
|
||
|
transform = CGAffineTransformTranslate(transform, 0, srcImg.size.height);
|
||
|
@@ -652,14 +664,14 @@
|
||
|
case UIImageOrientationUpMirrored:
|
||
|
break;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
switch (srcImg.imageOrientation) {
|
||
|
case UIImageOrientationUpMirrored:
|
||
|
case UIImageOrientationDownMirrored:
|
||
|
transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
|
||
|
transform = CGAffineTransformScale(transform, -1, 1);
|
||
|
break;
|
||
|
-
|
||
|
+
|
||
|
case UIImageOrientationLeftMirrored:
|
||
|
case UIImageOrientationRightMirrored:
|
||
|
transform = CGAffineTransformTranslate(transform, srcImg.size.height, 0);
|
||
|
@@ -671,7 +683,7 @@
|
||
|
case UIImageOrientationRight:
|
||
|
break;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
CGContextRef ctx = CGBitmapContextCreate(NULL, srcImg.size.width, srcImg.size.height, CGImageGetBitsPerComponent(srcImg.CGImage), 0, CGImageGetColorSpace(srcImg.CGImage), CGImageGetBitmapInfo(srcImg.CGImage));
|
||
|
CGContextConcatCTM(ctx, transform);
|
||
|
switch (srcImg.imageOrientation) {
|
||
|
@@ -681,12 +693,12 @@
|
||
|
case UIImageOrientationRightMirrored:
|
||
|
CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.height,srcImg.size.width), srcImg.CGImage);
|
||
|
break;
|
||
|
-
|
||
|
+
|
||
|
default:
|
||
|
CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.width,srcImg.size.height), srcImg.CGImage);
|
||
|
break;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
|
||
|
UIImage *img = [UIImage imageWithCGImage:cgimg];
|
||
|
CGContextRelease(ctx);
|
||
|
@@ -701,7 +713,7 @@
|
||
|
NSError *error = nil;
|
||
|
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
|
||
|
forKey: NSURLIsExcludedFromBackupKey error: &error];
|
||
|
-
|
||
|
+
|
||
|
if(!success){
|
||
|
NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
|
||
|
}
|