Honor attachment filenames.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent dd3250a9e8
commit 0018d0040b

@ -22,7 +22,8 @@ class AttachmentApprovalViewController: UIViewController {
@available(*, unavailable, message:"use attachment: constructor instead.") @available(*, unavailable, message:"use attachment: constructor instead.")
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
self.attachment = SignalAttachment.genericAttachment(data: nil, self.attachment = SignalAttachment.genericAttachment(data: nil,
dataUTI: kUTTypeContent as String) dataUTI: kUTTypeContent as String,
filename:nil)
super.init(coder: aDecoder) super.init(coder: aDecoder)
assertionFailure() assertionFailure()
} }

@ -162,8 +162,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)sendRandomAttachment:(TSThread *)thread + (void)sendRandomAttachment:(TSThread *)thread
uti:(NSString *)uti { uti:(NSString *)uti {
OWSMessageSender *messageSender = [Environment getCurrent].messageSender; OWSMessageSender *messageSender = [Environment getCurrent].messageSender;
SignalAttachment *attachment = [SignalAttachment genericAttachmentWithData:[self createRandomNSDataOfSize:256] SignalAttachment *attachment =
dataUTI:uti]; [SignalAttachment genericAttachmentWithData:[self createRandomNSDataOfSize:256] dataUTI:uti filename:nil];
[ThreadUtil sendMessageWithAttachment:attachment [ThreadUtil sendMessageWithAttachment:attachment
inThread:thread inThread:thread
messageSender:messageSender]; messageSender:messageSender];

@ -44,6 +44,7 @@
#import "UIViewController+CameraPermissions.h" #import "UIViewController+CameraPermissions.h"
#import "UIViewController+OWS.h" #import "UIViewController+OWS.h"
#import <AddressBookUI/AddressBookUI.h> #import <AddressBookUI/AddressBookUI.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <ContactsUI/CNContactViewController.h> #import <ContactsUI/CNContactViewController.h>
#import <JSQMessagesViewController/JSQMessagesBubbleImage.h> #import <JSQMessagesViewController/JSQMessagesBubbleImage.h>
#import <JSQMessagesViewController/JSQMessagesBubbleImageFactory.h> #import <JSQMessagesViewController/JSQMessagesBubbleImageFactory.h>
@ -2176,11 +2177,37 @@ typedef enum : NSUInteger {
- (void)imagePickerController:(UIImagePickerController *)picker - (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
{ {
OWSAssert([NSThread isMainThread]);
[UIUtil modalCompletionBlock](); [UIUtil modalCompletionBlock]();
[self resetFrame]; [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<NSString *, id> *)info
filename:(NSString *)filename
{
OWSAssert([NSThread isMainThread]);
void (^failedToPickAttachment)(NSError *error) = ^void(NSError *error) { void (^failedToPickAttachment)(NSError *error) = ^void(NSError *error) {
DDLogError(@"failed to pick attachment with error: %@", error); DDLogError(@"failed to pick attachment with error: %@", error);
}; };
@ -2192,20 +2219,22 @@ typedef enum : NSUInteger {
NSURL *videoURL = info[UIImagePickerControllerMediaURL]; NSURL *videoURL = info[UIImagePickerControllerMediaURL];
[self dismissViewControllerAnimated:YES [self dismissViewControllerAnimated:YES
completion:^{ completion:^{
[self sendQualityAdjustedAttachmentForVideo:videoURL]; [self sendQualityAdjustedAttachmentForVideo:videoURL filename:filename];
}]; }];
} else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { } else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
// Static Image captured from camera // Static Image captured from camera
UIImage *imageFromCamera = [info[UIImagePickerControllerOriginalImage] normalizedImage]; UIImage *imageFromCamera = [info[UIImagePickerControllerOriginalImage] normalizedImage];
[self dismissViewControllerAnimated:YES [self dismissViewControllerAnimated:YES
completion:^{ completion:^{
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
if (imageFromCamera) { if (imageFromCamera) {
SignalAttachment *attachment = [SignalAttachment imageAttachmentWithImage:imageFromCamera SignalAttachment *attachment =
dataUTI:(NSString *) kUTTypeJPEG]; [SignalAttachment imageAttachmentWithImage:imageFromCamera
dataUTI:(NSString *)kUTTypeJPEG
filename:filename];
if (!attachment || if (!attachment ||
[attachment hasError]) { [attachment hasError]) {
DDLogWarn(@"%@ %s Invalid attachment: %@.", DDLogWarn(@"%@ %s Invalid attachment: %@.",
@ -2247,9 +2276,9 @@ typedef enum : NSUInteger {
return failedToPickAttachment(assetFetchingError); return failedToPickAttachment(assetFetchingError);
} }
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
SignalAttachment *attachment = [SignalAttachment imageAttachmentWithData:imageData SignalAttachment *attachment =
dataUTI:dataUTI]; [SignalAttachment imageAttachmentWithData:imageData dataUTI:dataUTI filename:filename];
[self dismissViewControllerAnimated:YES [self dismissViewControllerAnimated:YES
completion:^{ completion:^{
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
@ -2312,7 +2341,8 @@ typedef enum : NSUInteger {
return [NSURL fileURLWithPath:basePath]; return [NSURL fileURLWithPath:basePath];
} }
- (void)sendQualityAdjustedAttachmentForVideo:(NSURL *)movieURL { - (void)sendQualityAdjustedAttachmentForVideo:(NSURL *)movieURL filename:(NSString *)filename
{
AVAsset *video = [AVAsset assetWithURL:movieURL]; AVAsset *video = [AVAsset assetWithURL:movieURL];
AVAssetExportSession *exportSession = AVAssetExportSession *exportSession =
[AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality]; [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality];
@ -2327,8 +2357,8 @@ typedef enum : NSUInteger {
exportSession.outputURL = compressedVideoUrl; exportSession.outputURL = compressedVideoUrl;
[exportSession exportAsynchronouslyWithCompletionHandler:^{ [exportSession exportAsynchronouslyWithCompletionHandler:^{
NSData *videoData = [NSData dataWithContentsOfURL:compressedVideoUrl]; NSData *videoData = [NSData dataWithContentsOfURL:compressedVideoUrl];
SignalAttachment *attachment = [SignalAttachment videoAttachmentWithData:videoData SignalAttachment *attachment =
dataUTI:(NSString *) kUTTypeMPEG4]; [SignalAttachment videoAttachmentWithData:videoData dataUTI:(NSString *)kUTTypeMPEG4 filename:filename];
if (!attachment || if (!attachment ||
[attachment hasError]) { [attachment hasError]) {
DDLogWarn(@"%@ %s Invalid attachment: %@.", DDLogWarn(@"%@ %s Invalid attachment: %@.",
@ -2567,8 +2597,8 @@ typedef enum : NSUInteger {
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag { - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
if (flag) { if (flag) {
NSData *audioData = [NSData dataWithContentsOfURL:recorder.url]; NSData *audioData = [NSData dataWithContentsOfURL:recorder.url];
SignalAttachment *attachment = [SignalAttachment audioAttachmentWithData:audioData SignalAttachment *attachment =
dataUTI:(NSString *) kUTTypeMPEG4Audio]; [SignalAttachment audioAttachmentWithData:audioData dataUTI:(NSString *)kUTTypeMPEG4Audio filename:nil];
if (!attachment || if (!attachment ||
[attachment hasError]) { [attachment hasError]) {
DDLogWarn(@"%@ %s Invalid attachment: %@.", DDLogWarn(@"%@ %s Invalid attachment: %@.",
@ -2671,6 +2701,7 @@ typedef enum : NSUInteger {
if (newGroupModel.groupImage) { if (newGroupModel.groupImage) {
[self.messageSender sendAttachmentData:UIImagePNGRepresentation(newGroupModel.groupImage) [self.messageSender sendAttachmentData:UIImagePNGRepresentation(newGroupModel.groupImage)
contentType:OWSMimeTypeImagePng contentType:OWSMimeTypeImagePng
filename:nil
inMessage:message inMessage:message
success:^{ success:^{
DDLogDebug(@"%@ Successfully sent group update with avatar", self.tag); DDLogDebug(@"%@ Successfully sent group update with avatar", self.tag);

@ -69,6 +69,9 @@ class SignalAttachment: NSObject {
// See: https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html // See: https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
let dataUTI: String 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 kOversizeTextAttachmentUTI = "org.whispersystems.oversize-text-attachment"
static let kUnknownTestAttachmentUTI = "org.whispersystems.unknown" static let kUnknownTestAttachmentUTI = "org.whispersystems.unknown"
@ -103,9 +106,10 @@ class SignalAttachment: NSObject {
// This method should not be called directly; use the factory // This method should not be called directly; use the factory
// methods instead. // methods instead.
internal required init(data: Data, dataUTI: String) { internal required init(data: Data, dataUTI: String, filename: String?) {
self.data = data self.data = data
self.dataUTI = dataUTI self.dataUTI = dataUTI
self.filename = filename
super.init() super.init()
} }
@ -268,7 +272,7 @@ class SignalAttachment: NSObject {
assertionFailure() assertionFailure()
return nil return nil
} }
return imageAttachment(data : data, dataUTI : dataUTI) return imageAttachment(data : data, dataUTI : dataUTI, filename: nil)
} }
} }
for dataUTI in videoUTISet { for dataUTI in videoUTISet {
@ -278,7 +282,7 @@ class SignalAttachment: NSObject {
assertionFailure() assertionFailure()
return nil return nil
} }
return videoAttachment(data : data, dataUTI : dataUTI) return videoAttachment(data : data, dataUTI : dataUTI, filename: nil)
} }
} }
for dataUTI in audioUTISet { for dataUTI in audioUTISet {
@ -288,7 +292,7 @@ class SignalAttachment: NSObject {
assertionFailure() assertionFailure()
return nil 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. // NOTE: The attachment returned by this method may not be valid.
// Check the attachment's error property. // 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(dataUTI.characters.count > 0)
assert(imageData != nil) assert(imageData != nil)
guard let imageData = imageData else { guard let imageData = imageData else {
let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI) let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI, filename: filename)
attachment.error = .missingData attachment.error = .missingData
return attachment return attachment
} }
let attachment = SignalAttachment(data : imageData, dataUTI: dataUTI) let attachment = SignalAttachment(data : imageData, dataUTI: dataUTI, filename: filename)
guard inputImageUTISet.contains(dataUTI) else { guard inputImageUTISet.contains(dataUTI) else {
attachment.error = .invalidFileFormat attachment.error = .invalidFileFormat
@ -373,7 +377,7 @@ class SignalAttachment: NSObject {
} }
Logger.verbose("\(TAG) Compressing attachment as image/jpeg") 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. // NOTE: The attachment returned by this method may nil or not be valid.
// Check the attachment's error property. // 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) assert(dataUTI.characters.count > 0)
guard let image = image else { guard let image = image else {
let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI) let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI, filename: filename)
attachment.error = .missingData attachment.error = .missingData
return attachment return attachment
} }
// Make a placeholder attachment on which to hang errors if necessary. // 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 attachment.image = image
Logger.verbose("\(TAG) Writing \(attachment.mimeType) as image/jpeg") 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) assert(attachment.error == nil)
var imageUploadQuality = defaultImageUploadQuality() var imageUploadQuality = defaultImageUploadQuality()
@ -445,7 +449,7 @@ class SignalAttachment: NSObject {
} }
if jpgImageData.count <= kMaxFileSizeImage { 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 recompressedAttachment.image = dstImage
return recompressedAttachment return recompressedAttachment
} }
@ -515,11 +519,12 @@ class SignalAttachment: NSObject {
// //
// NOTE: The attachment returned by this method may not be valid. // NOTE: The attachment returned by this method may not be valid.
// Check the attachment's error property. // 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, return newAttachment(data : data,
dataUTI : dataUTI, dataUTI : dataUTI,
validUTISet : videoUTISet, validUTISet : videoUTISet,
maxFileSize : kMaxFileSizeVideo) maxFileSize : kMaxFileSizeVideo,
filename : filename)
} }
// MARK: Audio Attachments // MARK: Audio Attachments
@ -528,11 +533,12 @@ class SignalAttachment: NSObject {
// //
// NOTE: The attachment returned by this method may not be valid. // NOTE: The attachment returned by this method may not be valid.
// Check the attachment's error property. // 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, return newAttachment(data : data,
dataUTI : dataUTI, dataUTI : dataUTI,
validUTISet : audioUTISet, validUTISet : audioUTISet,
maxFileSize : kMaxFileSizeAudio) maxFileSize : kMaxFileSizeAudio,
filename : filename)
} }
// MARK: Oversize Text Attachments // MARK: Oversize Text Attachments
@ -545,7 +551,8 @@ class SignalAttachment: NSObject {
return newAttachment(data : text?.data(using: .utf8), return newAttachment(data : text?.data(using: .utf8),
dataUTI : kOversizeTextAttachmentUTI, dataUTI : kOversizeTextAttachmentUTI,
validUTISet : nil, validUTISet : nil,
maxFileSize : kMaxFileSizeGeneric) maxFileSize : kMaxFileSizeGeneric,
filename : nil)
} }
// MARK: Generic Attachments // MARK: Generic Attachments
@ -554,11 +561,12 @@ class SignalAttachment: NSObject {
// //
// NOTE: The attachment returned by this method may not be valid. // NOTE: The attachment returned by this method may not be valid.
// Check the attachment's error property. // 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, return newAttachment(data : data,
dataUTI : dataUTI, dataUTI : dataUTI,
validUTISet : nil, validUTISet : nil,
maxFileSize : kMaxFileSizeGeneric) maxFileSize : kMaxFileSizeGeneric,
filename : filename)
} }
// MARK: Helper Methods // MARK: Helper Methods
@ -566,17 +574,18 @@ class SignalAttachment: NSObject {
private class func newAttachment(data: Data?, private class func newAttachment(data: Data?,
dataUTI: String, dataUTI: String,
validUTISet: Set<String>?, validUTISet: Set<String>?,
maxFileSize: Int) -> SignalAttachment { maxFileSize: Int,
filename: String?) -> SignalAttachment {
assert(dataUTI.characters.count > 0) assert(dataUTI.characters.count > 0)
assert(data != nil) assert(data != nil)
guard let data = data else { guard let data = data else {
let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI) let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI, filename: filename)
attachment.error = .missingData attachment.error = .missingData
return attachment return attachment
} }
let attachment = SignalAttachment(data : data, dataUTI: dataUTI) let attachment = SignalAttachment(data : data, dataUTI: dataUTI, filename: filename)
if let validUTISet = validUTISet { if let validUTISet = validUTISet {
guard validUTISet.contains(dataUTI) else { guard validUTISet.contains(dataUTI) else {

@ -78,7 +78,8 @@ NS_ASSUME_NONNULL_BEGIN
} }
[messageSender sendAttachmentData:attachment.data [messageSender sendAttachmentData:attachment.data
contentType:[attachment mimeType] contentType:attachment.mimeType
filename:attachment.filename
inMessage:message inMessage:message
success:^{ success:^{
DDLogDebug(@"%@ Successfully sent message attachment.", self.tag); DDLogDebug(@"%@ Successfully sent message attachment.", self.tag);

Loading…
Cancel
Save