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
This commit is contained in:
Michael Kirk 2016-07-21 17:15:34 -07:00 committed by GitHub
parent 3c2846274c
commit 86f06593d8
2 changed files with 74 additions and 69 deletions

View File

@ -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 = "<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>"; };
@ -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 */,

View File

@ -9,7 +9,6 @@
#import "AppDelegate.h"
#import <AddressBookUI/AddressBookUI.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <ContactsUI/CNContactViewController.h>
#import <MobileCoreServices/UTCoreTypes.h>
#import <SignalServiceKit/TSAccountManager.h>
@ -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<NSString *, id> *)info {
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)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"];
}
}];
}
}