Fix "can't reattach saved GIF" (and others) (#1288)

The root of the problem is we were using the deprecated ALAssetsLibrary
framework, which couldn't find certain assets.

By using the photos framework not only are we able to find these
assets, but it also cleans up our code:
* no more copying byte buffers
* no more detecting file type

// FREEBIE
pull/1/head
Michael Kirk 9 years ago committed by GitHub
parent 3c2846274c
commit 86f06593d8

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0DD55B166906AF3368995978 /* libPods-Signal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 80CD5E19DD23200E7926EEA7 /* libPods-Signal.a */; }; 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 */; }; 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 */; }; 453D28B31D32B87100D523F0 /* OWSErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B01D32B87100D523F0 /* OWSErrorMessage.m */; };
453D28B41D32B87100D523F0 /* OWSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B21D32B87100D523F0 /* OWSInfoMessage.m */; }; 453D28B41D32B87100D523F0 /* OWSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B21D32B87100D523F0 /* OWSInfoMessage.m */; };
453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */; }; 453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */; };
@ -504,6 +505,7 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference 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 = "<group>"; }; 4526BD481CA61C8D00166BC8 /* OWSMessageEditing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageEditing.h; sourceTree = "<group>"; };
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; }; 453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
453D28AF1D32B87100D523F0 /* OWSErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSErrorMessage.h; sourceTree = "<group>"; }; 453D28AF1D32B87100D523F0 /* OWSErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSErrorMessage.h; sourceTree = "<group>"; };
@ -1062,6 +1064,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */,
B6B226971BE4B7D200860F4D /* ContactsUI.framework in Frameworks */, B6B226971BE4B7D200860F4D /* ContactsUI.framework in Frameworks */,
B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */, B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */,
FC3BD9881A30A790005B96BB /* Social.framework in Frameworks */, FC3BD9881A30A790005B96BB /* Social.framework in Frameworks */,
@ -2068,6 +2071,7 @@
D221A08C169C9E5E00537ABF /* Frameworks */ = { D221A08C169C9E5E00537ABF /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4520D8D41D417D8E00123472 /* Photos.framework */,
B6B226961BE4B7D200860F4D /* ContactsUI.framework */, B6B226961BE4B7D200860F4D /* ContactsUI.framework */,
B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */, B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */,
FC3BD9871A30A790005B96BB /* Social.framework */, FC3BD9871A30A790005B96BB /* Social.framework */,

@ -9,7 +9,6 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import <AddressBookUI/AddressBookUI.h> #import <AddressBookUI/AddressBookUI.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <ContactsUI/CNContactViewController.h> #import <ContactsUI/CNContactViewController.h>
#import <MobileCoreServices/UTCoreTypes.h> #import <MobileCoreServices/UTCoreTypes.h>
#import <SignalServiceKit/TSAccountManager.h> #import <SignalServiceKit/TSAccountManager.h>
@ -52,6 +51,8 @@
#import "UIFont+OWS.h" #import "UIFont+OWS.h"
#import "UIUtil.h" #import "UIUtil.h"
@import Photos;
#define kYapDatabaseRangeLength 50 #define kYapDatabaseRangeLength 50
#define kYapDatabaseRangeMaxLength 300 #define kYapDatabaseRangeMaxLength 300
#define kYapDatabaseRangeMinLength 20 #define kYapDatabaseRangeMinLength 20
@ -1498,79 +1499,79 @@ typedef enum : NSUInteger {
/* /*
* Fetching data from UIImagePickerController * Fetching data from UIImagePickerController
*/ */
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info { - (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
{
[UIUtil modalCompletionBlock](); [UIUtil modalCompletionBlock]();
[self resetFrame]; [self resetFrame];
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType]; void (^failedToPickAttachment)(NSError *error) = ^void(NSError *error) {
if (CFStringCompare((__bridge_retained CFStringRef)mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) { DDLogError(@"failed to pick attachment with error: %@", error);
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL]; };
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]; [self sendQualityAdjustedAttachment:videoURL];
} else { } else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) // Static Image captured from camera
{
// Image captured from camera UIImage *imageFromCamera = [info[UIImagePickerControllerOriginalImage] normalizedImage];
UIImage *pictureCamera = [[info objectForKey:UIImagePickerControllerOriginalImage] normalizedImage]; if (imageFromCamera) {
if (pictureCamera) { [self sendMessageAttachment:[self qualityAdjustedAttachmentForImage:imageFromCamera] ofType:@"image/jpeg"];
DDLogVerbose(@"Sending picture attachement ...");
[self sendMessageAttachment:[self qualityAdjustedAttachmentForImage:pictureCamera] ofType:@"image/jpeg"];
}
} else { } else {
// Image picked from library failedToPickAttachment(nil);
// Send image as NSData to accommodate both static and animated images }
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; } else {
[library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] // Non-Video image picked from library
resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation]; NSURL *assetURL = info[UIImagePickerControllerReferenceURL];
Byte *img_buffer = (Byte *)malloc((unsigned long)representation.size); PHAsset *asset = [[PHAsset fetchAssetsWithALAssetURLs:@[ assetURL ] options:nil] lastObject];
NSUInteger length_buffered = if (!asset) {
[representation getBytes:img_buffer fromOffset:0 length:(unsigned long)representation.size error:nil]; return failedToPickAttachment(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);
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);
}];
} }
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"];
}
}];
} }
} }

Loading…
Cancel
Save