diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 319303bf0..85cadd6f9 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 344F2F671E57A932000D9322 /* UIViewController+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 344F2F661E57A932000D9322 /* UIViewController+OWS.m */; }; 34535D821E256BE9008A4747 /* UIView+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34535D811E256BE9008A4747 /* UIView+OWS.m */; }; 345671011E89A5F1006EE662 /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671001E89A5F1006EE662 /* ThreadUtil.m */; }; + 3456710A1E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671091E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m */; }; 34802DD71E899CFB0032EA1D /* DebugUITableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34802DD61E899CFB0032EA1D /* DebugUITableViewController.m */; }; 348A08421E6A044E0057E290 /* MessagesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 348A08411E6A044E0057E290 /* MessagesViewController.xib */; }; 348A08441E6A1D2C0057E290 /* OWSMessagesToolbarContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 348A08431E6A1D2C0057E290 /* OWSMessagesToolbarContentView.xib */; }; @@ -404,6 +405,8 @@ 34535D811E256BE9008A4747 /* UIView+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+OWS.m"; sourceTree = ""; }; 345670FF1E89A5F1006EE662 /* ThreadUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadUtil.h; sourceTree = ""; }; 345671001E89A5F1006EE662 /* ThreadUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadUtil.m; sourceTree = ""; }; + 345671081E8A9F5D006EE662 /* TSGenericAttachmentAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGenericAttachmentAdapter.h; sourceTree = ""; }; + 345671091E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGenericAttachmentAdapter.m; sourceTree = ""; }; 34802DD51E899CFB0032EA1D /* DebugUITableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUITableViewController.h; sourceTree = ""; }; 34802DD61E899CFB0032EA1D /* DebugUITableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUITableViewController.m; sourceTree = ""; }; 348A08411E6A044E0057E290 /* MessagesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessagesViewController.xib; sourceTree = ""; }; @@ -1428,6 +1431,8 @@ 4CE0E3751B95453C007210CF /* TSAnimatedAdapter.h */, 4CE0E3761B954546007210CF /* TSAnimatedAdapter.m */, B6D3CBCE1C1376BE00C039DF /* TSContentAdapters.h */, + 345671081E8A9F5D006EE662 /* TSGenericAttachmentAdapter.h */, + 345671091E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m */, B62D53F51A23CCAD009AAF82 /* TSMessageAdapter.h */, B62D53F61A23CCAD009AAF82 /* TSMessageAdapter.m */, B6A3EB491A423B3800B2236B /* TSPhotoAdapter.h */, @@ -2156,6 +2161,7 @@ 458E38311D6682450094BD24 /* OWSQRCodeScanningViewController.m in Sources */, 451A13B11E13DED2000A50FD /* CallNotificationsAdapter.swift in Sources */, 76EB062418170B33006006FC /* PriorityQueue.m in Sources */, + 3456710A1E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m in Sources */, 450DF2091E0DD2C6003D14BE /* UserNotificationsAdaptee.swift in Sources */, B6BADBE71B88D1AC0086A80D /* LockInteractionController.m in Sources */, 76EB061A18170B33006006FC /* DiscardingLog.m in Sources */, diff --git a/Signal/Images.xcassets/file-black-60.imageset/Contents.json b/Signal/Images.xcassets/file-black-60.imageset/Contents.json new file mode 100644 index 000000000..9dd859a99 --- /dev/null +++ b/Signal/Images.xcassets/file-black-60.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "file-icon-60@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "file-icon-60@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "file-icon-60@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@1x.png b/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@1x.png new file mode 100644 index 000000000..873b27287 Binary files /dev/null and b/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@1x.png differ diff --git a/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@2x.png b/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@2x.png new file mode 100644 index 000000000..fd1009293 Binary files /dev/null and b/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@2x.png differ diff --git a/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@3x.png b/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@3x.png new file mode 100644 index 000000000..98f2d35e4 Binary files /dev/null and b/Signal/Images.xcassets/file-black-60.imageset/file-icon-60@3x.png differ diff --git a/Signal/Images.xcassets/file-white-60.imageset/Contents.json b/Signal/Images.xcassets/file-white-60.imageset/Contents.json new file mode 100644 index 000000000..a57cb66ce --- /dev/null +++ b/Signal/Images.xcassets/file-white-60.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "file-white-60@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "file-white-60@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "file-white-60@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/file-white-60.imageset/file-white-60@1x.png b/Signal/Images.xcassets/file-white-60.imageset/file-white-60@1x.png new file mode 100644 index 000000000..5940a14f5 Binary files /dev/null and b/Signal/Images.xcassets/file-white-60.imageset/file-white-60@1x.png differ diff --git a/Signal/Images.xcassets/file-white-60.imageset/file-white-60@2x.png b/Signal/Images.xcassets/file-white-60.imageset/file-white-60@2x.png new file mode 100644 index 000000000..8581139d8 Binary files /dev/null and b/Signal/Images.xcassets/file-white-60.imageset/file-white-60@2x.png differ diff --git a/Signal/Images.xcassets/file-white-60.imageset/file-white-60@3x.png b/Signal/Images.xcassets/file-white-60.imageset/file-white-60@3x.png new file mode 100644 index 000000000..be6e88ec1 Binary files /dev/null and b/Signal/Images.xcassets/file-white-60.imageset/file-white-60@3x.png differ diff --git a/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m index 69ff941d4..770a0086e 100644 --- a/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m +++ b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m @@ -1,8 +1,11 @@ -// Copyright (c) 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSMessagesBubblesSizeCalculator.h" #import "OWSCall.h" #import "OWSDisplayedMessageCollectionViewCell.h" +#import "TSGenericAttachmentAdapter.h" #import "TSMessageAdapter.h" #import "UIFont+OWS.h" #import "tgmath.h" // generic math allows fmax to handle CGFLoat correctly on 32 & 64bit. diff --git a/Signal/src/Models/TSMessageAdapaters/AttachmentUploadView.m b/Signal/src/Models/TSMessageAdapaters/AttachmentUploadView.m index 539e4ba99..7dcb8ff49 100644 --- a/Signal/src/Models/TSMessageAdapaters/AttachmentUploadView.m +++ b/Signal/src/Models/TSMessageAdapaters/AttachmentUploadView.m @@ -79,6 +79,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)dealloc { + [_maskLayer removeFromSuperlayer]; + [_progressView removeFromSuperview]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } diff --git a/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.h index 1c94cfc66..465e20e16 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.h @@ -11,10 +11,6 @@ - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment incoming:(BOOL)incoming; -- (BOOL)isImage; -- (BOOL)isAudio; -- (BOOL)isVideo; - @property NSString *attachmentId; @property NSData *fileData; diff --git a/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m index 9a5962e9a..a74896ead 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m @@ -94,20 +94,6 @@ return [self ows_adjustBubbleSize:[super mediaViewDisplaySize] forImage:self.image]; } -- (BOOL)isImage { - return YES; -} - - -- (BOOL)isAudio { - return NO; -} - - -- (BOOL)isVideo { - return NO; -} - #pragma mark - OWSMessageEditing Protocol - (BOOL)canPerformEditingAction:(SEL)action diff --git a/Signal/src/Models/TSMessageAdapaters/TSGenericAttachmentAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSGenericAttachmentAdapter.h new file mode 100644 index 000000000..947a66d0b --- /dev/null +++ b/Signal/src/Models/TSMessageAdapaters/TSGenericAttachmentAdapter.h @@ -0,0 +1,14 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSMessageEditing.h" +#import + +@class TSAttachmentStream; + +@interface TSGenericAttachmentAdapter : JSQMediaItem + +- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment incoming:(BOOL)incoming; + +@end diff --git a/Signal/src/Models/TSMessageAdapaters/TSGenericAttachmentAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSGenericAttachmentAdapter.m new file mode 100644 index 000000000..3627ef66e --- /dev/null +++ b/Signal/src/Models/TSMessageAdapaters/TSGenericAttachmentAdapter.m @@ -0,0 +1,196 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "TSGenericAttachmentAdapter.h" +#import "AttachmentUploadView.h" +#import "TSAttachmentStream.h" +#import "UIColor+JSQMessages.h" +#import "UIColor+OWS.h" +#import "UIFont+OWS.h" +#import +#import +#import +#import + +@interface TSGenericAttachmentAdapter () + +@property (nonatomic) UIView *cachedMediaView; +@property (nonatomic) TSAttachmentStream *attachment; +@property (nonatomic) AttachmentUploadView *attachmentUploadView; +@property (nonatomic) BOOL incoming; +@property (nonatomic) NSString *attachmentId; + +@end + +@implementation TSGenericAttachmentAdapter + +- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment incoming:(BOOL)incoming +{ + self = [super init]; + + if (self) { + _attachment = attachment; + _attachmentId = attachment.uniqueId; + _incoming = incoming; + } + + return self; +} + +- (void)clearCachedMediaViews +{ + [super clearCachedMediaViews]; + _cachedMediaView = nil; +} + +- (void)setAppliesMediaViewMaskAsOutgoing:(BOOL)appliesMediaViewMaskAsOutgoing +{ + [super setAppliesMediaViewMaskAsOutgoing:appliesMediaViewMaskAsOutgoing]; + _cachedMediaView = nil; +} + +// TODO: Should we override hash or mediaHash? +- (NSUInteger)mediaHash +{ + return [self.attachment.uniqueId hash]; +} + +#pragma mark - JSQMessageMediaData protocol + +- (CGFloat)iconSize +{ + return 60.f; +} + +- (CGFloat)hMargin +{ + return 10.f; +} + +- (CGFloat)vMargin +{ + return 10.f; +} + +- (UIFont *)attachmentLabelFont +{ + return [UIFont ows_regularFontWithSize:11.f]; +} + +- (UIFont *)fileTypeLabelFont +{ + return [UIFont ows_mediumFontWithSize:16.f]; +} + +- (UIView *)mediaView +{ + if (_cachedMediaView == nil) { + CGSize viewSize = [self mediaViewDisplaySize]; + UIColor *textColor = (self.incoming ? [UIColor blackColor] : [UIColor whiteColor]); + + JSQMessagesBubbleImageFactory *bubbleFactory = [[JSQMessagesBubbleImageFactory alloc] init]; + JSQMessagesBubbleImage *bubbleImageData = (self.incoming + ? [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]] + : [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor ows_materialBlueColor]]); + UIImage *bubbleImage = [bubbleImageData messageBubbleImage]; + OWSAssert(bubbleImage); + UIImageView *bubbleImageView = [[UIImageView alloc] initWithImage:bubbleImage]; + _cachedMediaView = bubbleImageView; + _cachedMediaView.frame = CGRectMake(0.f, 0.f, viewSize.width, viewSize.height); + + const CGFloat kBubbleTailWidth = 6.f; + CGRect contentFrame = CGRectMake(self.incoming ? kBubbleTailWidth : 0.f, + self.vMargin, + viewSize.width - kBubbleTailWidth, + viewSize.height - self.vMargin * 2.f); + + UIImage *image = [UIImage imageNamed:(self.incoming ? @"file-black-60" : @"file-white-60")]; + OWSAssert(image); + UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; + CGRect iconFrame = CGRectMake(round(contentFrame.origin.x + (contentFrame.size.width - self.iconSize) * 0.5f), + round(contentFrame.origin.y), + self.iconSize, + self.iconSize); + imageView.frame = iconFrame; + [_cachedMediaView addSubview:imageView]; + + NSString *fileExtension = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType]; + if (fileExtension.length < 1) { + fileExtension = NSLocalizedString(@"GENERIC_ATTACHMENT_DEFAULT_TYPE", + @"A default label for attachment whose file extension cannot be determined."); + } + + UILabel *fileTypeLabel = [UILabel new]; + fileTypeLabel.text = fileExtension.uppercaseString; + fileTypeLabel.textColor = textColor; + fileTypeLabel.lineBreakMode = NSLineBreakByTruncatingTail; + fileTypeLabel.font = [self fileTypeLabelFont]; + CGRect fileTypeLabelFrame = CGRectZero; + fileTypeLabelFrame.size = [fileTypeLabel sizeThatFits:CGSizeZero]; + fileTypeLabelFrame.size.width = floor(MIN(self.iconSize * 0.5f, fileTypeLabelFrame.size.width)); + // Center on icon. + fileTypeLabelFrame.origin.x + = round(iconFrame.origin.x + (iconFrame.size.width - fileTypeLabelFrame.size.width) * 0.5f); + fileTypeLabelFrame.origin.y + = round(iconFrame.origin.y + (iconFrame.size.height - fileTypeLabelFrame.size.height) * 0.5f + 5); + fileTypeLabel.frame = fileTypeLabelFrame; + [_cachedMediaView addSubview:fileTypeLabel]; + + UILabel *attachmentLabel = [UILabel new]; + attachmentLabel.text = NSLocalizedString(@"GENERIC_ATTACHMENT_LABEL", @"A label for generic attachments."); + attachmentLabel.textColor = [textColor colorWithAlphaComponent:0.85f]; + attachmentLabel.lineBreakMode = NSLineBreakByTruncatingTail; + attachmentLabel.font = [self attachmentLabelFont]; + [attachmentLabel sizeToFit]; + CGRect attachmentLabelFrame = CGRectZero; + attachmentLabelFrame.size = attachmentLabel.bounds.size; + attachmentLabelFrame.origin.x + = round(contentFrame.origin.x + (contentFrame.size.width - attachmentLabelFrame.size.width) * 0.5f); + attachmentLabelFrame.origin.y + = round(contentFrame.origin.y + contentFrame.size.height - attachmentLabelFrame.size.height); + attachmentLabel.frame = attachmentLabelFrame; + [_cachedMediaView addSubview:attachmentLabel]; + + if (!self.incoming) { + self.attachmentUploadView = [[AttachmentUploadView alloc] initWithAttachment:self.attachment + superview:_cachedMediaView + attachmentStateCallback:nil]; + } + } + + return _cachedMediaView; +} + +- (CGSize)mediaViewDisplaySize +{ + const CGFloat kVSpacing = 1.f; + return CGSizeMake(100, ceil(self.iconSize + self.attachmentLabelFont.lineHeight + kVSpacing + self.vMargin * 2)); +} + +#pragma mark - OWSMessageEditing Protocol + +- (BOOL)canPerformEditingAction:(SEL)action +{ + if (action == @selector(copy:)) { + NSString *utiType = [MIMETypeUtil utiTypeForMIMEType:self.attachment.contentType]; + return utiType.length > 0; + } + return NO; +} + +- (void)performEditingAction:(SEL)action +{ + if (action == @selector(copy:)) { + NSString *utiType = [MIMETypeUtil utiTypeForMIMEType:self.attachment.contentType]; + OWSAssert(utiType.length > 0); + NSData *data = [NSData dataWithContentsOfURL:self.attachment.mediaURL]; + [UIPasteboard.generalPasteboard setData:data forPasteboardType:utiType]; + } else { + // Shouldn't get here, as only supported actions should be exposed via canPerformEditingAction + NSString *actionString = NSStringFromSelector(action); + DDLogError(@"'%@' action unsupported for %@: attachmentId=%@", actionString, [self class], self.attachmentId); + } +} + +@end diff --git a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m index 92bac48d8..bf3082fa4 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m @@ -4,20 +4,21 @@ #import "AttachmentSharing.h" #import "OWSCall.h" +#import "Signal-Swift.h" #import "TSAttachmentPointer.h" #import "TSAttachmentStream.h" #import "TSCall.h" #import "TSContactThread.h" #import "TSContentAdapters.h" #import "TSErrorMessage.h" +#import "TSGenericAttachmentAdapter.h" #import "TSGroupThread.h" #import "TSIncomingMessage.h" #import "TSInfoMessage.h" #import "TSOutgoingMessage.h" -#import "Signal-Swift.h" +#import "TSOversizeTextAttachmentAdapter.h" #import - @interface TSMessageAdapter () // --- @@ -131,7 +132,25 @@ if ([attachment isKindOfClass:[TSAttachmentStream class]]) { TSAttachmentStream *stream = (TSAttachmentStream *)attachment; - if ([stream isAnimated]) { + if ([attachment.contentType isEqualToString:OWSMimeTypeOversizeTextMessage]) { + NSData *textData = [NSData dataWithContentsOfURL:stream.mediaURL]; + NSString *fullText = [[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding]; + // TODO: Tune this value. + const NSUInteger kMaxTextDisplayLength = 256; + NSString *displayText = fullText; + if (fullText.length > kMaxTextDisplayLength) { + // Trim whitespace before _AND_ after slicing the snipper from the string. + NSString *snippet = + [[[fullText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] + substringWithRange:NSMakeRange(0, kMaxTextDisplayLength)] + stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + displayText = + [NSString stringWithFormat:NSLocalizedString(@"OVERSIZE_TEXT_DISPLAY_FORMAT", + @"A display format for oversize text messages."), + snippet]; + } + adapter.messageBody = displayText; + } else if ([stream isAnimated]) { adapter.mediaItem = [[TSAnimatedAdapter alloc] initWithAttachment:stream incoming:isIncomingAttachment]; adapter.mediaItem.appliesMediaViewMaskAsOutgoing = @@ -143,13 +162,19 @@ adapter.mediaItem.appliesMediaViewMaskAsOutgoing = [interaction isKindOfClass:[TSOutgoingMessage class]]; break; - } else { + } else if ([stream isVideo]) { adapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:stream incoming:[interaction isKindOfClass:[TSIncomingMessage class]]]; adapter.mediaItem.appliesMediaViewMaskAsOutgoing = [interaction isKindOfClass:[TSOutgoingMessage class]]; break; + } else { + adapter.mediaItem = [[TSGenericAttachmentAdapter alloc] + initWithAttachment:stream + incoming:[interaction isKindOfClass:[TSIncomingMessage class]]]; + adapter.mediaItem.appliesMediaViewMaskAsOutgoing = YES; + break; } } else if ([attachment isKindOfClass:[TSAttachmentPointer class]]) { TSAttachmentPointer *pointer = (TSAttachmentPointer *)attachment; diff --git a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h index 2e40ad11e..b824c2a8c 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h @@ -11,10 +11,6 @@ - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment incoming:(BOOL)incoming; -- (BOOL)isImage; -- (BOOL)isAudio; -- (BOOL)isVideo; - @property TSAttachmentStream *attachment; @property NSString *attachmentId; diff --git a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m index 7e3ebbb02..71dcb51e2 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m @@ -80,20 +80,6 @@ return [self ows_adjustBubbleSize:[super mediaViewDisplaySize] forImage:self.image]; } -- (BOOL)isImage { - return YES; -} - - -- (BOOL)isAudio { - return NO; -} - - -- (BOOL)isVideo { - return NO; -} - #pragma mark - OWSMessageEditing Protocol - (BOOL)canPerformEditingAction:(SEL)action diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h index 51ed816d6..0829cdd0a 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h @@ -15,7 +15,6 @@ - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment incoming:(BOOL)incoming; -- (BOOL)isImage; - (BOOL)isAudio; - (BOOL)isVideo; - (void)setAudioProgressFromFloat:(float)progress; diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m index eec46ed1a..93b7b60e8 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m @@ -46,10 +46,6 @@ return self; } -- (BOOL)isImage { - return NO; -} - - (BOOL)isAudio { return [MIMETypeUtil isSupportedAudioMIMEType:_contentType]; } diff --git a/Signal/src/view controllers/DebugUITableViewController.m b/Signal/src/view controllers/DebugUITableViewController.m index da109a9b9..17e3534c6 100644 --- a/Signal/src/view controllers/DebugUITableViewController.m +++ b/Signal/src/view controllers/DebugUITableViewController.m @@ -3,8 +3,9 @@ // #import "DebugUITableViewController.h" -#import "ThreadUtil.h" #import "Environment.h" +#import "Signal-Swift.h" +#import "ThreadUtil.h" #import NS_ASSUME_NONNULL_BEGIN @@ -220,7 +221,7 @@ NSString * const kDebugUITableCellIdentifier = @"kDebugUITableCellIdentifier"; return self.class.tag; } -#pragma mark - Factory and presentation +#pragma mark - Factory Methods + (void)presentDebugUIForThread:(TSThread *)thread fromViewController:(UIViewController *)fromViewController { @@ -232,11 +233,24 @@ NSString * const kDebugUITableCellIdentifier = @"kDebugUITableCellIdentifier"; [contents addSection:[OWSTableSection sectionWithTitle:@"Messages View" items:@[ - [OWSTableItem actionWithTitle:@"Send 100 messages" + [OWSTableItem actionWithTitle:@"Send 10 messages (1/sec.)" + actionBlock:^{ + [DebugUITableViewController sendTextMessage:10 + thread:thread]; + }], + [OWSTableItem actionWithTitle:@"Send 100 messages (1/sec.)" actionBlock:^{ [DebugUITableViewController sendTextMessage:100 thread:thread]; }], + [OWSTableItem actionWithTitle:@"Send text/x-signal-plain" + actionBlock:^{ + [DebugUITableViewController sendOversizeTextMessage:thread]; + }], + [OWSTableItem actionWithTitle:@"Send unknown/mimetype" + actionBlock:^{ + [DebugUITableViewController sendUnknownMimetypeAttachment:thread]; + }], ]]]; DebugUITableViewController *viewController = [DebugUITableViewController new]; @@ -259,6 +273,39 @@ NSString * const kDebugUITableCellIdentifier = @"kDebugUITableCellIdentifier"; }); } ++ (void)sendOversizeTextMessage:(TSThread *)thread { + OWSMessageSender *messageSender = [Environment getCurrent].messageSender; + NSString *message = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse consequat, ligula et tincidunt mattis, nisl risus ultricies justo, vitae dictum augue risus vel ante. Suspendisse convallis bibendum lectus. Etiam molestie nisi ac orci sodales sollicitudin vitae eu quam. Morbi lacinia scelerisque risus. Quisque sagittis mauris enim, ac vestibulum dui commodo quis. Nullam at commodo nisl, ut pulvinar dui. Nunc tempus volutpat sagittis. Vestibulum eget maximus sem, sit amet tristique ex posuere."; + SignalAttachment *attachment = [SignalAttachment oversizeTextAttachmentWithText:message]; + [ThreadUtil sendMessageWithAttachment:attachment + inThread:thread + messageSender:messageSender]; +} + ++ (NSData*)createRandomNSDataOfSize:(size_t)size +{ + OWSAssert(size % 4 == 0); + + NSMutableData* data = [NSMutableData dataWithCapacity:size]; + for (size_t i = 0; i < size / 4; ++i) + { + u_int32_t randomBits = arc4random(); + [data appendBytes:(void *)&randomBits length:4]; + } + return data; +} + ++ (void)sendUnknownMimetypeAttachment:(TSThread *)thread { + OWSMessageSender *messageSender = [Environment getCurrent].messageSender; + SignalAttachment *attachment = [SignalAttachment genericAttachmentWithData:[self createRandomNSDataOfSize:256] + dataUTI:SignalAttachment.kUnknownTestAttachmentUTI]; + [ThreadUtil sendMessageWithAttachment:attachment + inThread:thread + messageSender:messageSender]; +} + +#pragma mark - Presentation + - (void)presentFromViewController:(UIViewController *)fromViewController { OWSAssert(fromViewController); diff --git a/Signal/src/view controllers/SignalAttachment.swift b/Signal/src/view controllers/SignalAttachment.swift index 27d9cf09c..57f6265f5 100644 --- a/Signal/src/view controllers/SignalAttachment.swift +++ b/Signal/src/view controllers/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 + static let kOversizeTextAttachmentUTI = "org.whispersystems.oversize-text-attachment" + static let kUnknownTestAttachmentUTI = "org.whispersystems.unknown" + var error: SignalAttachmentError? { didSet { AssertIsOnMainThread() @@ -137,6 +140,12 @@ class SignalAttachment: NSObject { // Returns the MIME type for this attachment or nil if no MIME type // can be identified. var mimeType: String? { + if dataUTI == SignalAttachment.kOversizeTextAttachmentUTI { + return OWSMimeTypeOversizeTextMessage + } + if dataUTI == SignalAttachment.kUnknownTestAttachmentUTI { + return OWSMimeTypeUnknownForTests + } let mimeType = UTTypeCopyPreferredTagWithClass(dataUTI as CFString, kUTTagClassMIMEType) guard mimeType != nil else { return nil @@ -147,6 +156,12 @@ class SignalAttachment: NSObject { // Returns the file extension for this attachment or nil if no file extension // can be identified. var fileExtension: String? { + if dataUTI == SignalAttachment.kOversizeTextAttachmentUTI || + dataUTI == SignalAttachment.kUnknownTestAttachmentUTI { + assert(false) + return "" + } + guard let fileExtension = UTTypeCopyPreferredTagWithClass(dataUTI as CFString, kUTTagClassFilenameExtension) else { return nil @@ -508,6 +523,19 @@ class SignalAttachment: NSObject { maxFileSize : kMaxFileSizeAudio) } + // MARK: Oversize Text Attachments + + // Factory method for oversize text attachments. + // + // NOTE: The attachment returned by this method may not be valid. + // Check the attachment's error property. + public class func oversizeTextAttachment(text: String?) -> SignalAttachment { + return newAttachment(data : text?.data(using: .utf8), + dataUTI : kOversizeTextAttachmentUTI, + validUTISet : nil, + maxFileSize : kMaxFileSizeGeneric) + } + // MARK: Generic Attachments // Factory method for generic attachments. diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 38c5757cb..a9ca26be4 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -97,6 +97,9 @@ /* No comment provided by engineer. */ "ATTACHMENT_QUEUED" = "New attachment queued for retrieval."; +/* A message indicating that an attachment of unknown type was received. */ +"ATTACHMENT_UNKNOWN_TYPE" = "Unknown attachment received"; + /* No comment provided by engineer. */ "AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to work properly. You can grant this permission in the Settings app >> Privacy >> Microphone >> Signal"; @@ -325,6 +328,12 @@ /* Accessibilty label for finishing new group */ "FINISH_GROUP_CREATION_LABEL" = "Finish creating group"; +/* A default label for attachment whose file extension cannot be determined. */ +"GENERIC_ATTACHMENT_DEFAULT_TYPE" = "?"; + +/* A label for generic attachments. */ +"GENERIC_ATTACHMENT_LABEL" = "Attachment"; + /* No comment provided by engineer. */ "GROUP_AVATAR_CHANGED" = "Avatar changed. "; @@ -601,6 +610,9 @@ /* No comment provided by engineer. */ "OUTGOING_INCOMPLETE_CALL" = "Incomplete outgoing call"; +/* A display format for oversize text messages. */ +"OVERSIZE_TEXT_DISPLAY_FORMAT" = "%@… [Tap For More]"; + /* Alert body when verifying with {{contact name}} */ "PRIVACY_VERIFICATION_FAILED_I_HAVE_WRONG_KEY_FOR_THEM" = "This doesn't look like your safety number with %@. Are you verifying the correct contact?";