diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 0292272a5..0414f1d85 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 0DD55B166906AF3368995978 /* libPods-Signal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 80CD5E19DD23200E7926EEA7 /* libPods-Signal.a */; }; 30209C98DABCE82064B4EAF5 /* libPods-SignalTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A33D3C7EB4B17BDBD47F0FCC /* libPods-SignalTests.a */; }; + 4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4520D8D41D417D8E00123472 /* Photos.framework */; }; 453D28B31D32B87100D523F0 /* OWSErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B01D32B87100D523F0 /* OWSErrorMessage.m */; }; 453D28B41D32B87100D523F0 /* OWSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B21D32B87100D523F0 /* OWSInfoMessage.m */; }; 453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */; }; @@ -504,6 +505,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 4520D8D41D417D8E00123472 /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; }; 4526BD481CA61C8D00166BC8 /* OWSMessageEditing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageEditing.h; sourceTree = ""; }; 453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = ""; }; 453D28AF1D32B87100D523F0 /* OWSErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSErrorMessage.h; sourceTree = ""; }; @@ -1062,6 +1064,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */, B6B226971BE4B7D200860F4D /* ContactsUI.framework in Frameworks */, B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */, FC3BD9881A30A790005B96BB /* Social.framework in Frameworks */, @@ -2068,6 +2071,7 @@ D221A08C169C9E5E00537ABF /* Frameworks */ = { isa = PBXGroup; children = ( + 4520D8D41D417D8E00123472 /* Photos.framework */, B6B226961BE4B7D200860F4D /* ContactsUI.framework */, B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */, FC3BD9871A30A790005B96BB /* Social.framework */, diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 4bea925cf..1b56814c0 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -9,7 +9,6 @@ #import "AppDelegate.h" #import -#import #import #import #import @@ -52,6 +51,8 @@ #import "UIFont+OWS.h" #import "UIUtil.h" +@import Photos; + #define kYapDatabaseRangeLength 50 #define kYapDatabaseRangeMaxLength 300 #define kYapDatabaseRangeMinLength 20 @@ -1498,79 +1499,79 @@ typedef enum : NSUInteger { /* * Fetching data from UIImagePickerController */ -- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { +- (void)imagePickerController:(UIImagePickerController *)picker + didFinishPickingMediaWithInfo:(NSDictionary *)info +{ [UIUtil modalCompletionBlock](); [self resetFrame]; - NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType]; - if (CFStringCompare((__bridge_retained CFStringRef)mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) { - NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL]; - [self sendQualityAdjustedAttachment:videoURL]; - } else { - if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) - { - // Image captured from camera - UIImage *pictureCamera = [[info objectForKey:UIImagePickerControllerOriginalImage] normalizedImage]; - if (pictureCamera) { - DDLogVerbose(@"Sending picture attachement ..."); - [self sendMessageAttachment:[self qualityAdjustedAttachmentForImage:pictureCamera] ofType:@"image/jpeg"]; - } - } else { - // Image picked from library - // Send image as NSData to accommodate both static and animated images - ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; - [library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] - resultBlock:^(ALAsset *asset) { - ALAssetRepresentation *representation = [asset defaultRepresentation]; - Byte *img_buffer = (Byte *)malloc((unsigned long)representation.size); - NSUInteger length_buffered = - [representation getBytes:img_buffer fromOffset:0 length:(unsigned long)representation.size error:nil]; - NSData *img_data = [NSData dataWithBytesNoCopy:img_buffer length:length_buffered]; - NSString *file_type; - switch (img_buffer[0]) { - case 0x89: - file_type = @"image/png"; - break; - case 0x47: - file_type = @"image/gif"; - break; - case 0x49: - case 0x4D: - file_type = @"image/tiff"; - break; - case 0x42: - file_type = @"image/bmp"; - break; - case 0xFF: - default: - file_type = @"image/jpeg"; - break; - } - DDLogVerbose(@"Picked image. Size in bytes: %lu; first byte: %02x (%c); detected filetype: %@", - (unsigned long)length_buffered, - img_buffer[0], - img_buffer[0], - file_type); + void (^failedToPickAttachment)(NSError *error) = ^void(NSError *error) { + DDLogError(@"failed to pick attachment with error: %@", error); + }; - if ([file_type isEqualToString:@"image/gif"] && img_data.length <= 5 * 1024 * 1024) { - // Media Size constraints lifted from Signal-Android (org/thoughtcrime/securesms/mms/PushMediaConstraints.java) - // GifMaxSize return 5 * MB; - // For reference, other media size limits we're not explicitly enforcing: - // ImageMaxSize return 420 * KB; - // VideoMaxSize return 100 * MB; - // getAudioMaxSize 100 * MB; - DDLogVerbose(@"Sending raw image/gif"); - [self sendMessageAttachment:img_data ofType:file_type]; - } else { - DDLogVerbose(@"Compressing attachment as image/jpeg"); - UIImage *pickedImage = [[UIImage alloc] initWithData:img_data]; - [self sendMessageAttachment:[self qualityAdjustedAttachmentForImage:pickedImage] ofType:@"image/jpeg"]; - } - } - failureBlock:^(NSError *error) { - DDLogVerbose(@"Couldn't get image asset: %@", error); - }]; + NSString *mediaType = info[UIImagePickerControllerMediaType]; + if ([mediaType isEqualToString:(__bridge NSString *)kUTTypeMovie]) { + // Video picked from library or captured with camera + + NSURL *videoURL = info[UIImagePickerControllerMediaURL]; + [self sendQualityAdjustedAttachment:videoURL]; + } else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { + // Static Image captured from camera + + UIImage *imageFromCamera = [info[UIImagePickerControllerOriginalImage] normalizedImage]; + if (imageFromCamera) { + [self sendMessageAttachment:[self qualityAdjustedAttachmentForImage:imageFromCamera] ofType:@"image/jpeg"]; + } else { + failedToPickAttachment(nil); } + } else { + // Non-Video image picked from library + + NSURL *assetURL = info[UIImagePickerControllerReferenceURL]; + PHAsset *asset = [[PHAsset fetchAssetsWithALAssetURLs:@[ assetURL ] options:nil] lastObject]; + if (!asset) { + return failedToPickAttachment(nil); + } + + PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; + options.synchronous = YES; // We're only fetching one asset. + options.networkAccessAllowed = YES; // iCloud OK + options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; // Don't need quick/dirty version + [[PHImageManager defaultManager] + requestImageDataForAsset:asset + options:options + resultHandler:^(NSData *_Nullable imageData, + NSString *_Nullable dataUTI, + UIImageOrientation orientation, + NSDictionary *_Nullable assetInfo) { + + NSError *assetFetchingError = assetInfo[PHImageErrorKey]; + if (assetFetchingError || !imageData) { + return failedToPickAttachment(assetFetchingError); + } + DDLogVerbose(@"Size in bytes: %lu; detected filetype: %@", imageData.length, dataUTI); + + if ([dataUTI isEqualToString:(__bridge NSString *)kUTTypeGIF] + && imageData.length <= 5 * 1024 * 1024) { + DDLogVerbose(@"Sending raw image/gif to retain any animation"); + /** + * Media Size constraints lifted from Signal-Android + * (org/thoughtcrime/securesms/mms/PushMediaConstraints.java) + * + * GifMaxSize return 5 * MB; + * For reference, other media size limits we're not explicitly enforcing: + * ImageMaxSize return 420 * KB; + * VideoMaxSize return 100 * MB; + * getAudioMaxSize 100 * MB; + */ + [self sendMessageAttachment:imageData ofType:@"image/gif"]; + } else { + DDLogVerbose(@"Compressing attachment as image/jpeg"); + UIImage *pickedImage = [[UIImage alloc] initWithData:imageData]; + [self sendMessageAttachment:[self qualityAdjustedAttachmentForImage:pickedImage] + ofType:@"image/jpeg"]; + } + }]; } }