diff --git a/Signal/src/ViewControllers/AttachmentApprovalViewController.swift b/Signal/src/ViewControllers/AttachmentApprovalViewController.swift index c2b9dca99..9df11cfe5 100644 --- a/Signal/src/ViewControllers/AttachmentApprovalViewController.swift +++ b/Signal/src/ViewControllers/AttachmentApprovalViewController.swift @@ -22,7 +22,8 @@ class AttachmentApprovalViewController: UIViewController { @available(*, unavailable, message:"use attachment: constructor instead.") required init?(coder aDecoder: NSCoder) { self.attachment = SignalAttachment.genericAttachment(data: nil, - dataUTI: kUTTypeContent as String) + dataUTI: kUTTypeContent as String, + filename:nil) super.init(coder: aDecoder) assertionFailure() } diff --git a/Signal/src/ViewControllers/DebugUITableViewController.m b/Signal/src/ViewControllers/DebugUITableViewController.m index 88e3ed94f..f4e9aa56a 100644 --- a/Signal/src/ViewControllers/DebugUITableViewController.m +++ b/Signal/src/ViewControllers/DebugUITableViewController.m @@ -162,8 +162,8 @@ NS_ASSUME_NONNULL_BEGIN + (void)sendRandomAttachment:(TSThread *)thread uti:(NSString *)uti { OWSMessageSender *messageSender = [Environment getCurrent].messageSender; - SignalAttachment *attachment = [SignalAttachment genericAttachmentWithData:[self createRandomNSDataOfSize:256] - dataUTI:uti]; + SignalAttachment *attachment = + [SignalAttachment genericAttachmentWithData:[self createRandomNSDataOfSize:256] dataUTI:uti filename:nil]; [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender]; diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index b901a7a73..e6418366b 100644 --- a/Signal/src/ViewControllers/MessagesViewController.m +++ b/Signal/src/ViewControllers/MessagesViewController.m @@ -44,6 +44,7 @@ #import "UIViewController+CameraPermissions.h" #import "UIViewController+OWS.h" #import +#import #import #import #import @@ -2176,11 +2177,37 @@ typedef enum : NSUInteger { - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { - OWSAssert([NSThread isMainThread]); - [UIUtil modalCompletionBlock](); [self resetFrame]; + NSURL *referenceURL = [info valueForKey:UIImagePickerControllerReferenceURL]; + if (!referenceURL) { + DDLogError(@"Could not retrieve reference URL for picked asset"); + OWSAssert(0); + return; + } + + ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset) { + ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation]; + NSString *filename = [imageRep filename]; + [self imagePickerController:picker didFinishPickingMediaWithInfo:info filename:filename]; + }; + + ALAssetsLibrary *assetslibrary = [[ALAssetsLibrary alloc] init]; + [assetslibrary assetForURL:referenceURL + resultBlock:resultblock + failureBlock:^(NSError *error) { + DDLogError(@"Error retrieving filename for asset: %@", error); + OWSAssert(0); + }]; +} + +- (void)imagePickerController:(UIImagePickerController *)picker + didFinishPickingMediaWithInfo:(NSDictionary *)info + filename:(NSString *)filename +{ + OWSAssert([NSThread isMainThread]); + void (^failedToPickAttachment)(NSError *error) = ^void(NSError *error) { DDLogError(@"failed to pick attachment with error: %@", error); }; @@ -2192,20 +2219,22 @@ typedef enum : NSUInteger { NSURL *videoURL = info[UIImagePickerControllerMediaURL]; [self dismissViewControllerAnimated:YES completion:^{ - [self sendQualityAdjustedAttachmentForVideo:videoURL]; + [self sendQualityAdjustedAttachmentForVideo:videoURL filename:filename]; }]; } else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { // Static Image captured from camera UIImage *imageFromCamera = [info[UIImagePickerControllerOriginalImage] normalizedImage]; - + [self dismissViewControllerAnimated:YES completion:^{ OWSAssert([NSThread isMainThread]); if (imageFromCamera) { - SignalAttachment *attachment = [SignalAttachment imageAttachmentWithImage:imageFromCamera - dataUTI:(NSString *) kUTTypeJPEG]; + SignalAttachment *attachment = + [SignalAttachment imageAttachmentWithImage:imageFromCamera + dataUTI:(NSString *)kUTTypeJPEG + filename:filename]; if (!attachment || [attachment hasError]) { DDLogWarn(@"%@ %s Invalid attachment: %@.", @@ -2247,9 +2276,9 @@ typedef enum : NSUInteger { return failedToPickAttachment(assetFetchingError); } OWSAssert([NSThread isMainThread]); - - SignalAttachment *attachment = [SignalAttachment imageAttachmentWithData:imageData - dataUTI:dataUTI]; + + SignalAttachment *attachment = + [SignalAttachment imageAttachmentWithData:imageData dataUTI:dataUTI filename:filename]; [self dismissViewControllerAnimated:YES completion:^{ OWSAssert([NSThread isMainThread]); @@ -2312,7 +2341,8 @@ typedef enum : NSUInteger { return [NSURL fileURLWithPath:basePath]; } -- (void)sendQualityAdjustedAttachmentForVideo:(NSURL *)movieURL { +- (void)sendQualityAdjustedAttachmentForVideo:(NSURL *)movieURL filename:(NSString *)filename +{ AVAsset *video = [AVAsset assetWithURL:movieURL]; AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality]; @@ -2327,8 +2357,8 @@ typedef enum : NSUInteger { exportSession.outputURL = compressedVideoUrl; [exportSession exportAsynchronouslyWithCompletionHandler:^{ NSData *videoData = [NSData dataWithContentsOfURL:compressedVideoUrl]; - SignalAttachment *attachment = [SignalAttachment videoAttachmentWithData:videoData - dataUTI:(NSString *) kUTTypeMPEG4]; + SignalAttachment *attachment = + [SignalAttachment videoAttachmentWithData:videoData dataUTI:(NSString *)kUTTypeMPEG4 filename:filename]; if (!attachment || [attachment hasError]) { DDLogWarn(@"%@ %s Invalid attachment: %@.", @@ -2567,8 +2597,8 @@ typedef enum : NSUInteger { - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag { if (flag) { NSData *audioData = [NSData dataWithContentsOfURL:recorder.url]; - SignalAttachment *attachment = [SignalAttachment audioAttachmentWithData:audioData - dataUTI:(NSString *) kUTTypeMPEG4Audio]; + SignalAttachment *attachment = + [SignalAttachment audioAttachmentWithData:audioData dataUTI:(NSString *)kUTTypeMPEG4Audio filename:nil]; if (!attachment || [attachment hasError]) { DDLogWarn(@"%@ %s Invalid attachment: %@.", @@ -2671,6 +2701,7 @@ typedef enum : NSUInteger { if (newGroupModel.groupImage) { [self.messageSender sendAttachmentData:UIImagePNGRepresentation(newGroupModel.groupImage) contentType:OWSMimeTypeImagePng + filename:nil inMessage:message success:^{ DDLogDebug(@"%@ Successfully sent group update with avatar", self.tag); diff --git a/Signal/src/ViewControllers/SignalAttachment.swift b/Signal/src/ViewControllers/SignalAttachment.swift index c3677dbab..7ab404eb6 100644 --- a/Signal/src/ViewControllers/SignalAttachment.swift +++ b/Signal/src/ViewControllers/SignalAttachment.swift @@ -69,6 +69,9 @@ class SignalAttachment: NSObject { // See: https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html let dataUTI: String + // An optional field that indicates the filename, if known, for this attachment. + let filename: String? + static let kOversizeTextAttachmentUTI = "org.whispersystems.oversize-text-attachment" static let kUnknownTestAttachmentUTI = "org.whispersystems.unknown" @@ -103,9 +106,10 @@ class SignalAttachment: NSObject { // This method should not be called directly; use the factory // methods instead. - internal required init(data: Data, dataUTI: String) { + internal required init(data: Data, dataUTI: String, filename: String?) { self.data = data self.dataUTI = dataUTI + self.filename = filename super.init() } @@ -268,7 +272,7 @@ class SignalAttachment: NSObject { assertionFailure() return nil } - return imageAttachment(data : data, dataUTI : dataUTI) + return imageAttachment(data : data, dataUTI : dataUTI, filename: nil) } } for dataUTI in videoUTISet { @@ -278,7 +282,7 @@ class SignalAttachment: NSObject { assertionFailure() return nil } - return videoAttachment(data : data, dataUTI : dataUTI) + return videoAttachment(data : data, dataUTI : dataUTI, filename: nil) } } for dataUTI in audioUTISet { @@ -288,7 +292,7 @@ class SignalAttachment: NSObject { assertionFailure() return nil } - return audioAttachment(data : data, dataUTI : dataUTI) + return audioAttachment(data : data, dataUTI : dataUTI, filename: nil) } } @@ -329,17 +333,17 @@ class SignalAttachment: NSObject { // // NOTE: The attachment returned by this method may not be valid. // Check the attachment's error property. - public class func imageAttachment(data imageData: Data?, dataUTI: String) -> SignalAttachment { + public class func imageAttachment(data imageData: Data?, dataUTI: String, filename: String?) -> SignalAttachment { assert(dataUTI.characters.count > 0) assert(imageData != nil) guard let imageData = imageData else { - let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI) + let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI, filename: filename) attachment.error = .missingData return attachment } - let attachment = SignalAttachment(data : imageData, dataUTI: dataUTI) + let attachment = SignalAttachment(data : imageData, dataUTI: dataUTI, filename: filename) guard inputImageUTISet.contains(dataUTI) else { attachment.error = .invalidFileFormat @@ -373,7 +377,7 @@ class SignalAttachment: NSObject { } Logger.verbose("\(TAG) Compressing attachment as image/jpeg") - return compressImageAsJPEG(image : image, attachment : attachment) + return compressImageAsJPEG(image : image, attachment : attachment, filename:filename) } } @@ -409,24 +413,24 @@ class SignalAttachment: NSObject { // // NOTE: The attachment returned by this method may nil or not be valid. // Check the attachment's error property. - public class func imageAttachment(image: UIImage?, dataUTI: String) -> SignalAttachment { + public class func imageAttachment(image: UIImage?, dataUTI: String, filename: String?) -> SignalAttachment { assert(dataUTI.characters.count > 0) guard let image = image else { - let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI) + let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI, filename: filename) attachment.error = .missingData return attachment } // Make a placeholder attachment on which to hang errors if necessary. - let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI) + let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI, filename: filename) attachment.image = image Logger.verbose("\(TAG) Writing \(attachment.mimeType) as image/jpeg") - return compressImageAsJPEG(image : image, attachment : attachment) + return compressImageAsJPEG(image : image, attachment : attachment, filename:filename) } - private class func compressImageAsJPEG(image: UIImage, attachment: SignalAttachment) -> SignalAttachment { + private class func compressImageAsJPEG(image: UIImage, attachment: SignalAttachment, filename: String?) -> SignalAttachment { assert(attachment.error == nil) var imageUploadQuality = defaultImageUploadQuality() @@ -445,7 +449,7 @@ class SignalAttachment: NSObject { } if jpgImageData.count <= kMaxFileSizeImage { - let recompressedAttachment = SignalAttachment(data : jpgImageData, dataUTI: kUTTypeJPEG as String) + let recompressedAttachment = SignalAttachment(data : jpgImageData, dataUTI: kUTTypeJPEG as String, filename: filename) recompressedAttachment.image = dstImage return recompressedAttachment } @@ -515,11 +519,12 @@ class SignalAttachment: NSObject { // // NOTE: The attachment returned by this method may not be valid. // Check the attachment's error property. - public class func videoAttachment(data: Data?, dataUTI: String) -> SignalAttachment { + public class func videoAttachment(data: Data?, dataUTI: String, filename: String?) -> SignalAttachment { return newAttachment(data : data, dataUTI : dataUTI, validUTISet : videoUTISet, - maxFileSize : kMaxFileSizeVideo) + maxFileSize : kMaxFileSizeVideo, + filename : filename) } // MARK: Audio Attachments @@ -528,11 +533,12 @@ class SignalAttachment: NSObject { // // NOTE: The attachment returned by this method may not be valid. // Check the attachment's error property. - public class func audioAttachment(data: Data?, dataUTI: String) -> SignalAttachment { + public class func audioAttachment(data: Data?, dataUTI: String, filename: String?) -> SignalAttachment { return newAttachment(data : data, dataUTI : dataUTI, validUTISet : audioUTISet, - maxFileSize : kMaxFileSizeAudio) + maxFileSize : kMaxFileSizeAudio, + filename : filename) } // MARK: Oversize Text Attachments @@ -545,7 +551,8 @@ class SignalAttachment: NSObject { return newAttachment(data : text?.data(using: .utf8), dataUTI : kOversizeTextAttachmentUTI, validUTISet : nil, - maxFileSize : kMaxFileSizeGeneric) + maxFileSize : kMaxFileSizeGeneric, + filename : nil) } // MARK: Generic Attachments @@ -554,11 +561,12 @@ class SignalAttachment: NSObject { // // NOTE: The attachment returned by this method may not be valid. // Check the attachment's error property. - public class func genericAttachment(data: Data?, dataUTI: String) -> SignalAttachment { + public class func genericAttachment(data: Data?, dataUTI: String, filename: String?) -> SignalAttachment { return newAttachment(data : data, dataUTI : dataUTI, validUTISet : nil, - maxFileSize : kMaxFileSizeGeneric) + maxFileSize : kMaxFileSizeGeneric, + filename : filename) } // MARK: Helper Methods @@ -566,17 +574,18 @@ class SignalAttachment: NSObject { private class func newAttachment(data: Data?, dataUTI: String, validUTISet: Set?, - maxFileSize: Int) -> SignalAttachment { + maxFileSize: Int, + filename: String?) -> SignalAttachment { assert(dataUTI.characters.count > 0) assert(data != nil) guard let data = data else { - let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI) + let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI, filename: filename) attachment.error = .missingData return attachment } - let attachment = SignalAttachment(data : data, dataUTI: dataUTI) + let attachment = SignalAttachment(data : data, dataUTI: dataUTI, filename: filename) if let validUTISet = validUTISet { guard validUTISet.contains(dataUTI) else { diff --git a/Signal/src/util/ThreadUtil.m b/Signal/src/util/ThreadUtil.m index 03c26ff31..ab623bee3 100644 --- a/Signal/src/util/ThreadUtil.m +++ b/Signal/src/util/ThreadUtil.m @@ -78,7 +78,8 @@ NS_ASSUME_NONNULL_BEGIN } [messageSender sendAttachmentData:attachment.data - contentType:[attachment mimeType] + contentType:attachment.mimeType + filename:attachment.filename inMessage:message success:^{ DDLogDebug(@"%@ Successfully sent message attachment.", self.tag);