Merge branch 'charlesmchen/constantBubbleSizes'

pull/2/head
Matthew Chen 6 years ago
commit 7bb4814224

@ -369,7 +369,6 @@
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */; };
452C7CA72037628B003D51A5 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170D51E315310003FC1F2 /* Weak.swift */; };
452D1AF12081059C00A67F7F /* StringAdditionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */; };
452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */; };
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */; };
452EC6E1205FF5DC000E787C /* Bench.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6E0205FF5DC000E787C /* Bench.swift */; };
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; };
@ -1093,7 +1092,6 @@
452B998F20A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactShareToExistingContactViewController.swift; sourceTree = "<group>"; };
452C468E1E427E200087B011 /* OutboundCallInitiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutboundCallInitiator.swift; sourceTree = "<group>"; };
452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAdditionsTest.swift; sourceTree = "<group>"; };
452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentPointerView.swift; sourceTree = "<group>"; };
452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryViewController.swift; sourceTree = "<group>"; };
452EC6E0205FF5DC000E787C /* Bench.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bench.swift; sourceTree = "<group>"; };
452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageFetcherJob.swift; sourceTree = "<group>"; };
@ -2419,7 +2417,6 @@
76EB052B18170B33006006FC /* Views */ = {
isa = PBXGroup;
children = (
452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */,
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */,
4C2F454E214C00E1004871FF /* AvatarTableViewCell.swift */,
4CA46F4B219CCC630038ABDE /* CaptionView.swift */,
@ -3602,7 +3599,6 @@
34B3F87B1E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift in Sources */,
3448E1622213585C004B052E /* OnboardingBaseViewController.swift in Sources */,
34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */,
452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */,
45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */,
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */,
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,

@ -1,13 +1,11 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@class TSAttachmentStream;
typedef void (^AttachmentStateBlock)(BOOL isAttachmentReady);
// This entity is used to display upload progress for outgoing
// attachments in conversation view cells.
//
@ -18,8 +16,7 @@ typedef void (^AttachmentStateBlock)(BOOL isAttachmentReady);
// * Disable any media view controls using a callback.
@interface AttachmentUploadView : UIView
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment
attachmentStateCallback:(AttachmentStateBlock _Nullable)attachmentStateCallback;
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment;
@end

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "AttachmentUploadView.h"
@ -21,8 +21,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) UILabel *progressLabel;
@property (nonatomic) AttachmentStateBlock _Nullable attachmentStateCallback;
@property (nonatomic) BOOL isAttachmentReady;
@property (nonatomic) CGFloat lastProgress;
@ -34,7 +32,6 @@ NS_ASSUME_NONNULL_BEGIN
@implementation AttachmentUploadView
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment
attachmentStateCallback:(AttachmentStateBlock _Nullable)attachmentStateCallback
{
self = [super init];
@ -42,7 +39,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssertDebug(attachment);
self.attachment = attachment;
self.attachmentStateCallback = attachmentStateCallback;
[self createContents];
@ -54,10 +50,6 @@ NS_ASSUME_NONNULL_BEGIN
_isAttachmentReady = self.attachment.isUploaded;
[self ensureViewState];
if (attachmentStateCallback) {
self.attachmentStateCallback(_isAttachmentReady);
}
}
return self;
}
@ -110,10 +102,6 @@ NS_ASSUME_NONNULL_BEGIN
_isAttachmentReady = isAttachmentReady;
[self ensureViewState];
if (self.attachmentStateCallback) {
self.attachmentStateCallback(isAttachmentReady);
}
}
- (void)setLastProgress:(CGFloat)lastProgress

@ -1,17 +1,17 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@class ConversationStyle;
@class TSAttachmentStream;
@class TSAttachment;
@protocol ConversationViewItem;
@interface OWSAudioMessageView : UIStackView
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream
- (instancetype)initWithAttachment:(TSAttachment *)attachment
isIncoming:(BOOL)isIncoming
viewItem:(id<ConversationViewItem>)viewItem
conversationStyle:(ConversationStyle *)conversationStyle;

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "OWSAudioMessageView.h"
@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSAudioMessageView ()
@property (nonatomic) TSAttachmentStream *attachmentStream;
@property (nonatomic) TSAttachment *attachment;
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
@property (nonatomic) BOOL isIncoming;
@property (nonatomic, weak) id<ConversationViewItem> viewItem;
@property (nonatomic, readonly) ConversationStyle *conversationStyle;
@ -30,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSAudioMessageView
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream
- (instancetype)initWithAttachment:(TSAttachment *)attachment
isIncoming:(BOOL)isIncoming
viewItem:(id<ConversationViewItem>)viewItem
conversationStyle:(ConversationStyle *)conversationStyle
@ -38,7 +39,10 @@ NS_ASSUME_NONNULL_BEGIN
self = [super init];
if (self) {
_attachmentStream = attachmentStream;
_attachment = attachment;
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
_attachmentStream = (TSAttachmentStream *)attachment;
}
_isIncoming = isIncoming;
_viewItem = viewItem;
_conversationStyle = conversationStyle;
@ -66,8 +70,6 @@ NS_ASSUME_NONNULL_BEGIN
- (CGFloat)audioDurationSeconds
{
OWSAssertDebug(self.viewItem.audioDurationSeconds > 0.f);
return self.viewItem.audioDurationSeconds;
}
@ -174,7 +176,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isVoiceMessage
{
return self.attachmentStream.isVoiceMessage;
return self.attachment.isVoiceMessage;
}
- (void)createContents
@ -190,13 +192,13 @@ NS_ASSUME_NONNULL_BEGIN
[self addArrangedSubview:self.audioPlayPauseButton];
[self.audioPlayPauseButton setContentHuggingHigh];
NSString *filename = self.attachmentStream.sourceFilename;
if (!filename) {
NSString *_Nullable filename = self.attachment.sourceFilename;
if (filename.length < 1) {
filename = [self.attachmentStream.originalFilePath lastPathComponent];
}
NSString *topText = [[filename stringByDeletingPathExtension] ows_stripped];
if (topText.length < 1) {
topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType].localizedUppercaseString;
topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType].localizedUppercaseString;
}
if (topText.length < 1) {
topText = NSLocalizedString(@"GENERIC_ATTACHMENT_LABEL", @"A label for generic attachments.");

@ -1,15 +1,15 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@class ConversationStyle;
@class TSAttachmentStream;
@class TSAttachment;
@interface OWSGenericAttachmentView : UIStackView
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming;
- (instancetype)initWithAttachment:(TSAttachment *)attachment isIncoming:(BOOL)isIncoming;
- (void)createContentsWithConversationStyle:(ConversationStyle *)conversationStyle;

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "OWSGenericAttachmentView.h"
@ -18,7 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSGenericAttachmentView ()
@property (nonatomic) TSAttachmentStream *attachmentStream;
@property (nonatomic) TSAttachment *attachment;
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
@property (nonatomic) BOOL isIncoming;
@property (nonatomic) UILabel *topLabel;
@property (nonatomic) UILabel *bottomLabel;
@ -29,12 +30,15 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSGenericAttachmentView
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming
- (instancetype)initWithAttachment:(TSAttachment *)attachment isIncoming:(BOOL)isIncoming
{
self = [super init];
if (self) {
_attachmentStream = attachmentStream;
_attachment = attachment;
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
_attachmentStream = (TSAttachmentStream *)attachment;
}
_isIncoming = isIncoming;
}
@ -105,13 +109,13 @@ NS_ASSUME_NONNULL_BEGIN
[self addArrangedSubview:imageView];
[imageView setContentHuggingHigh];
NSString *filename = self.attachmentStream.sourceFilename;
NSString *_Nullable filename = self.attachment.sourceFilename;
if (!filename) {
filename = [[self.attachmentStream originalFilePath] lastPathComponent];
}
NSString *fileExtension = filename.pathExtension;
if (fileExtension.length < 1) {
fileExtension = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType];
fileExtension = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType];
}
UILabel *fileTypeLabel = [UILabel new];
@ -132,9 +136,9 @@ NS_ASSUME_NONNULL_BEGIN
labelsView.alignment = UIStackViewAlignmentLeading;
[self addArrangedSubview:labelsView];
NSString *topText = [self.attachmentStream.sourceFilename ows_stripped];
NSString *topText = [self.attachment.sourceFilename ows_stripped];
if (topText.length < 1) {
topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachmentStream.contentType].localizedUppercaseString;
topText = [MIMETypeUtil fileExtensionForMIMEType:self.attachment.contentType].localizedUppercaseString;
}
if (topText.length < 1) {
topText = NSLocalizedString(@"GENERIC_ATTACHMENT_LABEL", @"A label for generic attachments.");
@ -147,12 +151,21 @@ NS_ASSUME_NONNULL_BEGIN
topLabel.font = [OWSGenericAttachmentView topLabelFont];
[labelsView addArrangedSubview:topLabel];
NSError *error;
unsigned long long fileSize =
[[NSFileManager defaultManager] attributesOfItemAtPath:[self.attachmentStream originalFilePath] error:&error]
.fileSize;
OWSAssertDebug(!error);
NSString *bottomText = [OWSFormat formatFileSize:fileSize];
unsigned long long fileSize = 0;
if (self.attachmentStream) {
NSError *error;
fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:[self.attachmentStream originalFilePath]
error:&error]
.fileSize;
OWSAssertDebug(!error);
}
// We don't want to show the file size while the attachment is downloading.
// To avoid layout jitter when the download completes, we reserve space in
// the layout using a whitespace string.
NSString *bottomText = @" ";
if (fileSize > 0) {
bottomText = [OWSFormat formatFileSize:fileSize];
}
UILabel *bottomLabel = [UILabel new];
self.bottomLabel = bottomLabel;
bottomLabel.text = bottomText;

@ -60,6 +60,15 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
@implementation OWSMessageBubbleView
#pragma mark - Dependencies
- (OWSAttachmentDownloads *)attachmentDownloads
{
return SSKEnvironment.shared.attachmentDownloads;
}
#pragma mark -
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
@ -168,22 +177,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
return self.viewItem.displayableBodyText;
}
- (nullable TSAttachmentStream *)attachmentStream
{
// This should always be valid for the appropriate cell types.
OWSAssertDebug(self.viewItem.attachmentStream);
return self.viewItem.attachmentStream;
}
- (nullable TSAttachmentPointer *)attachmentPointer
{
// This should always be valid for the appropriate cell types.
OWSAssertDebug(self.viewItem.attachmentPointer);
return self.viewItem.attachmentPointer;
}
- (TSMessage *)message
{
OWSAssertDebug([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
@ -276,21 +269,20 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
case OWSMessageCellType_TextOnlyMessage:
break;
case OWSMessageCellType_Audio:
OWSAssertDebug(self.viewItem.attachmentStream);
bodyMediaView = [self loadViewForAudio];
break;
case OWSMessageCellType_GenericAttachment:
bodyMediaView = [self loadViewForGenericAttachment];
break;
case OWSMessageCellType_DownloadingAttachment:
bodyMediaView = [self loadViewForDownloadingAttachment];
break;
case OWSMessageCellType_ContactShare:
bodyMediaView = [self loadViewForContactShare];
break;
case OWSMessageCellType_MediaMessage:
bodyMediaView = [self loadViewForMediaAlbum];
break;
case OWSMessageCellType_OversizeTextDownloading:
bodyMediaView = [self loadViewForOversizeTextDownload];
break;
}
if (bodyMediaView) {
@ -572,8 +564,8 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
case OWSMessageCellType_TextOnlyMessage:
case OWSMessageCellType_Audio:
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
case OWSMessageCellType_ContactShare:
case OWSMessageCellType_OversizeTextDownloading:
return NO;
case OWSMessageCellType_MediaMessage:
return YES;
@ -587,9 +579,9 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
return NO;
case OWSMessageCellType_Audio:
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
case OWSMessageCellType_ContactShare:
case OWSMessageCellType_MediaMessage:
case OWSMessageCellType_OversizeTextDownloading:
return YES;
}
}
@ -837,16 +829,17 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
- (UIView *)loadViewForAudio
{
OWSAssertDebug(self.attachmentStream);
OWSAssertDebug([self.attachmentStream isAudio]);
TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer);
OWSAssertDebug(attachment);
OWSAssertDebug([attachment isAudio]);
OWSAudioMessageView *audioMessageView = [[OWSAudioMessageView alloc] initWithAttachment:self.attachmentStream
OWSAudioMessageView *audioMessageView = [[OWSAudioMessageView alloc] initWithAttachment:attachment
isIncoming:self.isIncoming
viewItem:self.viewItem
conversationStyle:self.conversationStyle];
self.viewItem.lastAudioMessageView = audioMessageView;
[audioMessageView createContents];
[self addAttachmentUploadViewIfNecessary];
[self addProgressViewsIfNecessary:audioMessageView];
self.loadCellContentBlock = ^{
// Do nothing.
@ -860,11 +853,12 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
- (UIView *)loadViewForGenericAttachment
{
OWSAssertDebug(self.viewItem.attachmentStream);
TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer);
OWSAssertDebug(attachment);
OWSGenericAttachmentView *attachmentView =
[[OWSGenericAttachmentView alloc] initWithAttachment:self.attachmentStream isIncoming:self.isIncoming];
[[OWSGenericAttachmentView alloc] initWithAttachment:attachment isIncoming:self.isIncoming];
[attachmentView createContentsWithConversationStyle:self.conversationStyle];
[self addAttachmentUploadViewIfNecessary];
[self addProgressViewsIfNecessary:attachmentView];
self.loadCellContentBlock = ^{
// Do nothing.
@ -876,20 +870,15 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
return attachmentView;
}
- (UIView *)loadViewForDownloadingAttachment
- (UIView *)loadViewForContactShare
{
OWSAssertDebug(self.attachmentPointer);
// TODO: We probably want to do something different for attachments
// being restored from backup.
AttachmentPointerView *downloadView =
[[AttachmentPointerView alloc] initWithAttachmentPointer:self.attachmentPointer
isIncoming:self.isIncoming
conversationStyle:self.conversationStyle];
OWSAssertDebug(self.viewItem.contactShare);
UIView *wrapper = [UIView new];
[wrapper addSubview:downloadView];
[downloadView autoPinEdgesToSuperviewEdges];
OWSContactShareView *contactShareView = [[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare
isIncoming:self.isIncoming
conversationStyle:self.conversationStyle];
[contactShareView createContents];
// TODO: Should we change appearance if contact avatar is uploading?
self.loadCellContentBlock = ^{
// Do nothing.
@ -898,18 +887,16 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
// Do nothing.
};
return wrapper;
return contactShareView;
}
- (UIView *)loadViewForContactShare
- (UIView *)loadViewForOversizeTextDownload
{
OWSAssertDebug(self.viewItem.contactShare);
// We can use an empty view. The progress views will display download
// progress or tap-to-retry UI.
UIView *attachmentView = [UIView new];
OWSContactShareView *contactShareView = [[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare
isIncoming:self.isIncoming
conversationStyle:self.conversationStyle];
[contactShareView createContents];
// TODO: Should we change appearance if contact avatar is uploading?
[self addProgressViewsIfNecessary:attachmentView];
self.loadCellContentBlock = ^{
// Do nothing.
@ -918,33 +905,101 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
// Do nothing.
};
return contactShareView;
return attachmentView;
}
- (void)addAttachmentUploadViewIfNecessary
- (void)addProgressViewsIfNecessary:(UIView *)bodyMediaView
{
[self addAttachmentUploadViewIfNecessaryWithAttachmentStateCallback:nil];
if (self.viewItem.attachmentStream) {
[self addUploadViewIfNecessary:bodyMediaView];
} else if (self.viewItem.attachmentPointer) {
[self addDownloadViewIfNecessary:bodyMediaView];
}
}
- (void)addAttachmentUploadViewIfNecessaryWithAttachmentStateCallback:
(nullable AttachmentStateBlock)attachmentStateCallback
- (void)addUploadViewIfNecessary:(UIView *)bodyMediaView
{
OWSAssertDebug(self.attachmentStream);
OWSAssertDebug(self.viewItem.attachmentStream);
if (!attachmentStateCallback) {
attachmentStateCallback = ^(BOOL isAttachmentReady) {
};
if (!self.isOutgoing) {
return;
}
if (self.viewItem.attachmentStream.isUploaded) {
return;
}
if (self.isOutgoing) {
if (!self.attachmentStream.isUploaded) {
AttachmentUploadView *attachmentUploadView =
[[AttachmentUploadView alloc] initWithAttachment:self.attachmentStream
attachmentStateCallback:attachmentStateCallback];
[self.bubbleView addSubview:attachmentUploadView];
[attachmentUploadView ows_autoPinToSuperviewEdges];
}
AttachmentUploadView *uploadView = [[AttachmentUploadView alloc] initWithAttachment:self.viewItem.attachmentStream];
[self.bubbleView addSubview:uploadView];
[uploadView autoPinEdgesToSuperviewEdges];
[uploadView setContentHuggingLow];
[uploadView setCompressionResistanceLow];
}
- (void)addDownloadViewIfNecessary:(UIView *)bodyMediaView
{
OWSAssertDebug(self.viewItem.attachmentPointer);
switch (self.viewItem.attachmentPointer.state) {
case TSAttachmentPointerStateFailed:
[self addTapToRetryView:bodyMediaView];
return;
case TSAttachmentPointerStateEnqueued:
case TSAttachmentPointerStateDownloading:
break;
}
switch (self.viewItem.attachmentPointer.pointerType) {
case TSAttachmentPointerTypeRestoring:
// TODO: Show "restoring" indicator and possibly progress.
return;
case TSAttachmentPointerTypeUnknown:
case TSAttachmentPointerTypeIncoming:
break;
}
NSString *_Nullable uniqueId = self.viewItem.attachmentPointer.uniqueId;
if (uniqueId.length < 1) {
OWSFailDebug(@"Missing uniqueId.");
return;
}
if ([self.attachmentDownloads downloadProgressForAttachmentId:uniqueId] == nil) {
OWSFailDebug(@"Missing download progress.");
return;
}
UIView *overlayView = [UIView new];
overlayView.backgroundColor = [self.bubbleColor colorWithAlphaComponent:0.5];
[bodyMediaView addSubview:overlayView];
[overlayView autoPinEdgesToSuperviewEdges];
[overlayView setContentHuggingLow];
[overlayView setCompressionResistanceLow];
MediaDownloadView *downloadView =
[[MediaDownloadView alloc] initWithAttachmentId:uniqueId radius:self.conversationStyle.maxMessageWidth * 0.1f];
bodyMediaView.layer.opacity = 0.5f;
[self.bubbleView addSubview:downloadView];
[downloadView autoPinEdgesToSuperviewEdges];
[downloadView setContentHuggingLow];
[downloadView setCompressionResistanceLow];
}
- (void)addTapToRetryView:(UIView *)bodyMediaView
{
OWSAssertDebug(self.viewItem.attachmentPointer);
// Hide the body media view, replace with "tap to retry" indicator.
UILabel *label = [UILabel new];
label.text = NSLocalizedString(
@"ATTACHMENT_DOWNLOADING_STATUS_FAILED", @"Status label when an attachment download has failed.");
label.font = UIFont.ows_dynamicTypeBodyFont;
label.textColor = Theme.secondaryColor;
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.textAlignment = NSTextAlignmentCenter;
label.backgroundColor = self.bubbleColor;
[bodyMediaView addSubview:label];
[label autoPinEdgesToSuperviewMargins];
[label setContentHuggingLow];
[label setCompressionResistanceLow];
}
- (void)showAttachmentErrorViewWithMediaView:(UIView *)mediaView
@ -1005,16 +1060,14 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
result = CGSizeMake(maxMessageWidth, OWSAudioMessageView.bubbleHeight);
break;
case OWSMessageCellType_GenericAttachment: {
OWSAssertDebug(self.viewItem.attachmentStream);
TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer);
OWSAssertDebug(attachment);
OWSGenericAttachmentView *attachmentView =
[[OWSGenericAttachmentView alloc] initWithAttachment:self.attachmentStream isIncoming:self.isIncoming];
[[OWSGenericAttachmentView alloc] initWithAttachment:attachment isIncoming:self.isIncoming];
[attachmentView createContentsWithConversationStyle:self.conversationStyle];
result = [attachmentView measureSizeWithMaxMessageWidth:maxMessageWidth];
break;
}
case OWSMessageCellType_DownloadingAttachment:
result = CGSizeMake(MIN(200, maxMessageWidth), [AttachmentPointerView measureHeight]);
break;
case OWSMessageCellType_ContactShare:
OWSAssertDebug(self.viewItem.contactShare);
@ -1027,8 +1080,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
if (self.viewItem.mediaAlbumItems.count == 1) {
// Honor the content aspect ratio for single media.
ConversationMediaAlbumItem *mediaAlbumItem = self.viewItem.mediaAlbumItems.firstObject;
if (mediaAlbumItem.attachmentStream && mediaAlbumItem.mediaSize.width > 0
&& mediaAlbumItem.mediaSize.height > 0) {
if (mediaAlbumItem.mediaSize.width > 0 && mediaAlbumItem.mediaSize.height > 0) {
CGSize mediaSize = mediaAlbumItem.mediaSize;
CGFloat contentAspectRatio = mediaSize.width / mediaSize.height;
// Clamp the aspect ratio so that very thin/wide content is presented
@ -1060,6 +1112,11 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
}
}
break;
case OWSMessageCellType_OversizeTextDownloading:
// There's no way to predict the size of the oversize text,
// so we just use a square bubble.
result = CGSizeMake(maxMessageWidth, maxMessageWidth);
break;
}
OWSAssertDebug(result.width <= maxMessageWidth);
@ -1392,29 +1449,26 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
{
OWSAssertDebug(self.delegate);
if (self.viewItem.attachmentPointer && self.viewItem.attachmentPointer.state == TSAttachmentPointerStateFailed) {
[self.delegate didTapFailedIncomingAttachment:self.viewItem];
return;
}
switch (self.cellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
case OWSMessageCellType_OversizeTextDownloading:
break;
case OWSMessageCellType_Audio:
OWSAssertDebug(self.viewItem.attachmentStream);
[self.delegate didTapAudioViewItem:self.viewItem attachmentStream:self.viewItem.attachmentStream];
if (self.viewItem.attachmentStream) {
[self.delegate didTapAudioViewItem:self.viewItem attachmentStream:self.viewItem.attachmentStream];
}
return;
case OWSMessageCellType_GenericAttachment:
OWSAssertDebug(self.viewItem.attachmentStream);
[AttachmentSharing showShareUIForAttachment:self.viewItem.attachmentStream];
break;
case OWSMessageCellType_DownloadingAttachment: {
TSAttachmentPointer *_Nullable attachmentPointer = self.viewItem.attachmentPointer;
OWSAssertDebug(attachmentPointer);
if (attachmentPointer.state == TSAttachmentPointerStateFailed) {
[self.delegate didTapFailedIncomingAttachment:self.viewItem];
if (self.viewItem.attachmentStream) {
[AttachmentSharing showShareUIForAttachment:self.viewItem.attachmentStream];
}
break;
}
case OWSMessageCellType_ContactShare:
[self.delegate didTapContactShareViewItem:self.viewItem];
break;

@ -12,9 +12,9 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) {
OWSMessageCellType_TextOnlyMessage,
OWSMessageCellType_Audio,
OWSMessageCellType_GenericAttachment,
OWSMessageCellType_DownloadingAttachment,
OWSMessageCellType_ContactShare,
OWSMessageCellType_MediaMessage,
OWSMessageCellType_OversizeTextDownloading,
};
NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);

@ -26,14 +26,14 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return @"OWSMessageCellType_Audio";
case OWSMessageCellType_GenericAttachment:
return @"OWSMessageCellType_GenericAttachment";
case OWSMessageCellType_DownloadingAttachment:
return @"OWSMessageCellType_DownloadingAttachment";
case OWSMessageCellType_Unknown:
return @"OWSMessageCellType_Unknown";
case OWSMessageCellType_ContactShare:
return @"OWSMessageCellType_ContactShare";
case OWSMessageCellType_MediaMessage:
return @"OWSMessageCellType_MediaMessage";
case OWSMessageCellType_OversizeTextDownloading:
return @"OWSMessageCellType_OversizeTextDownloading";
}
}
@ -591,10 +591,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
TSAttachment *_Nullable oversizeTextAttachment = [message oversizeTextAttachmentWithTransaction:transaction];
if (oversizeTextAttachment != nil && [oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) {
if ([oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *oversizeTextAttachmentStream = (TSAttachmentStream *)oversizeTextAttachment;
self.displayableBodyText = [self displayableBodyTextForOversizeTextAttachment:oversizeTextAttachmentStream
interactionId:message.uniqueId];
} else if ([oversizeTextAttachment isKindOfClass:[TSAttachmentPointer class]]) {
TSAttachmentPointer *oversizeTextAttachmentPointer = (TSAttachmentPointer *)oversizeTextAttachment;
// TODO: Handle backup restore.
self.messageCellType = OWSMessageCellType_OversizeTextDownloading;
self.attachmentPointer = (TSAttachmentPointer *)oversizeTextAttachmentPointer;
return;
} else {
NSString *_Nullable bodyText = [message bodyTextWithTransaction:transaction];
if (bodyText) {
@ -638,7 +644,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
self.messageCellType = OWSMessageCellType_GenericAttachment;
}
} else if ([mediaAttachment isKindOfClass:[TSAttachmentPointer class]]) {
self.messageCellType = OWSMessageCellType_DownloadingAttachment;
if ([mediaAttachment isAudio]) {
self.audioDurationSeconds = 0;
self.messageCellType = OWSMessageCellType_Audio;
} else {
self.messageCellType = OWSMessageCellType_GenericAttachment;
}
self.attachmentPointer = (TSAttachmentPointer *)mediaAttachment;
} else {
OWSFailDebug(@"Unknown attachment type");
@ -704,10 +715,15 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
: nil);
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentPointer *attachmentPointer = (TSAttachmentPointer *)attachment;
CGSize mediaSize = CGSizeZero;
if (attachmentPointer.mediaSize.width > 0 && attachmentPointer.mediaSize.height > 0) {
mediaSize = attachmentPointer.mediaSize;
}
[mediaAlbumItems addObject:[[ConversationMediaAlbumItem alloc] initWithAttachment:attachment
attachmentStream:nil
caption:caption
mediaSize:CGSizeZero]];
mediaSize:mediaSize]];
continue;
}
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
@ -849,6 +865,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
- (void)copyTextAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_TextOnlyMessage:
case OWSMessageCellType_Audio:
@ -858,10 +879,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[UIPasteboard.generalPasteboard setString:self.displayableBodyText.fullText];
break;
}
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_Unknown: {
OWSFailDebug(@"No text to copy");
break;
@ -871,11 +888,19 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSFailDebug(@"Not implemented yet");
break;
}
case OWSMessageCellType_OversizeTextDownloading:
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
}
- (void)copyMediaAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -888,10 +913,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[self copyAttachmentToPasteboard:self.attachmentStream];
break;
}
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_MediaMessage: {
if (self.mediaAlbumItems.count == 1) {
ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject;
@ -904,6 +925,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSFailDebug(@"Can't copy media album");
break;
}
case OWSMessageCellType_OversizeTextDownloading:
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
}
@ -926,6 +950,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
- (void)shareMediaAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -936,10 +965,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_GenericAttachment:
[AttachmentSharing showShareUIForAttachment:self.attachmentStream];
break;
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_MediaMessage: {
// TODO: We need a "canShareMediaAction" method.
OWSAssertDebug(self.mediaAlbumItems);
@ -956,11 +981,19 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[AttachmentSharing showShareUIForAttachments:attachmentStreams completion:nil];
break;
}
case OWSMessageCellType_OversizeTextDownloading:
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
return;
}
}
- (BOOL)canCopyMedia
{
if (self.attachmentPointer != nil) {
// The attachment is still downloading.
return NO;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -969,7 +1002,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Audio:
return NO;
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
case OWSMessageCellType_MediaMessage: {
if (self.mediaAlbumItems.count == 1) {
ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject;
@ -979,11 +1011,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
return NO;
}
case OWSMessageCellType_OversizeTextDownloading:
return NO;
}
}
- (BOOL)canSaveMedia
{
if (self.attachmentPointer != nil) {
// The attachment is still downloading.
return NO;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -992,7 +1031,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Audio:
return NO;
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
return NO;
case OWSMessageCellType_MediaMessage: {
for (ConversationMediaAlbumItem *mediaAlbumItem in self.mediaAlbumItems) {
@ -1014,11 +1052,17 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
return NO;
}
case OWSMessageCellType_OversizeTextDownloading:
return NO;
}
}
- (void)saveMediaAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -1031,14 +1075,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_GenericAttachment:
OWSFailDebug(@"Cannot save media data.");
break;
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_MediaMessage: {
[self saveMediaAlbumItems];
break;
}
case OWSMessageCellType_OversizeTextDownloading:
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
return;
}
}
@ -1101,6 +1144,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
- (BOOL)hasMediaActionContent
{
if (self.attachmentPointer != nil) {
// The attachment is still downloading.
return NO;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -1109,11 +1157,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Audio:
case OWSMessageCellType_GenericAttachment:
return self.attachmentStream != nil;
case OWSMessageCellType_DownloadingAttachment: {
return NO;
}
case OWSMessageCellType_MediaMessage:
return self.firstValidAlbumAttachment != nil;
case OWSMessageCellType_OversizeTextDownloading:
return NO;
}
}

@ -3802,7 +3802,8 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
sourceFilename:@"test.mp3"
caption:nil
albumMessageId:nil
attachmentType:TSAttachmentTypeDefault];
attachmentType:TSAttachmentTypeDefault
mediaSize:CGSizeZero];
pointer.state = TSAttachmentPointerStateFailed;
[pointer saveWithTransaction:transaction];
// MJK - should be safe to remove this senderTimestamp
@ -4701,7 +4702,8 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
sourceFilename:fakeAssetLoader.filename
caption:nil
albumMessageId:nil
attachmentType:TSAttachmentTypeDefault];
attachmentType:TSAttachmentTypeDefault
mediaSize:CGSizeZero];
attachmentPointer.state = TSAttachmentPointerStateFailed;
[attachmentPointer saveWithTransaction:transaction];
return attachmentPointer;

@ -1,145 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalServiceKit
import SignalMessaging
class AttachmentPointerView: UIStackView {
let isIncoming: Bool
let attachmentPointer: TSAttachmentPointer
let conversationStyle: ConversationStyle
let progressView = OWSProgressView()
let nameLabel = UILabel()
let statusLabel = UILabel()
let filename: String
let genericFilename = NSLocalizedString("ATTACHMENT_DEFAULT_FILENAME", comment: "Generic filename for an attachment with no known name")
var progress: CGFloat = 0 {
didSet {
self.progressView.progress = progress
}
}
@objc
required init(attachmentPointer: TSAttachmentPointer, isIncoming: Bool, conversationStyle: ConversationStyle) {
self.attachmentPointer = attachmentPointer
self.isIncoming = isIncoming
self.conversationStyle = conversationStyle
let attachmentPointerFilename = attachmentPointer.sourceFilename
if let filename = attachmentPointerFilename, !filename.isEmpty {
self.filename = filename
} else {
self.filename = genericFilename
}
super.init(frame: CGRect.zero)
createSubviews()
updateViews()
if attachmentPointer.state == .downloading {
NotificationCenter.default.addObserver(self,
selector: #selector(attachmentDownloadProgress(_:)),
name: NSNotification.Name.attachmentDownloadProgress,
object: nil)
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc internal func attachmentDownloadProgress(_ notification: Notification) {
guard let attachmentId = attachmentPointer.uniqueId else {
owsFailDebug("Missing attachment id.")
return
}
guard let progress = (notification as NSNotification).userInfo?[kAttachmentDownloadProgressKey] as? NSNumber else {
owsFailDebug("Attachment download notification missing progress.")
return
}
guard let notificationAttachmentId = (notification as NSNotification).userInfo?[kAttachmentDownloadAttachmentIDKey] as? String else {
owsFailDebug("Attachment download notification missing attachment id.")
return
}
guard notificationAttachmentId == attachmentId else {
return
}
self.progress = CGFloat(progress.floatValue)
}
@available(*, unavailable, message: "use init(call:) constructor instead.")
required init(coder aDecoder: NSCoder) {
notImplemented()
}
private static var vSpacing: CGFloat = 5
private class func nameFont() -> UIFont { return UIFont.ows_dynamicTypeBody }
private class func statusFont() -> UIFont { return UIFont.ows_dynamicTypeCaption1 }
private static var progressWidth: CGFloat = 80
private static var progressHeight: CGFloat = 6
func createSubviews() {
progressView.autoSetDimension(.width, toSize: AttachmentPointerView.progressWidth)
progressView.autoSetDimension(.height, toSize: AttachmentPointerView.progressHeight)
// truncate middle to be sure we include file extension
nameLabel.lineBreakMode = .byTruncatingMiddle
nameLabel.textAlignment = .center
nameLabel.textColor = self.textColor
nameLabel.font = AttachmentPointerView.nameFont()
statusLabel.textAlignment = .center
statusLabel.adjustsFontSizeToFitWidth = true
statusLabel.numberOfLines = 2
statusLabel.textColor = self.textColor
statusLabel.font = AttachmentPointerView.statusFont()
self.axis = .vertical
self.spacing = AttachmentPointerView.vSpacing
addArrangedSubview(nameLabel)
addArrangedSubview(progressView)
addArrangedSubview(statusLabel)
}
func updateViews() {
let emoji = TSAttachment.emoji(forMimeType: self.attachmentPointer.contentType)
nameLabel.text = "\(emoji) \(self.filename)"
statusLabel.text = {
switch self.attachmentPointer.state {
case .enqueued:
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_QUEUED", comment: "Status label when an attachment is enqueued, but hasn't yet started downloading")
case .downloading:
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_IN_PROGRESS", comment: "Status label when an attachment is currently downloading")
case .failed:
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_FAILED", comment: "Status label when an attachment download has failed.")
}
}()
if attachmentPointer.state == .downloading {
progressView.isHidden = false
progressView.autoSetDimension(.height, toSize: 8)
} else {
progressView.isHidden = true
progressView.autoSetDimension(.height, toSize: 0)
}
}
var textColor: UIColor {
return conversationStyle.bubbleTextColor(isIncoming: isIncoming)
}
@objc
public class func measureHeight() -> CGFloat {
return ceil(nameFont().lineHeight +
statusFont().lineHeight +
progressHeight +
vSpacing * 2)
}
}

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "TSAttachment.h"
@ -36,6 +36,8 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
// messages received from other clients
@property (nullable, nonatomic, readonly) NSData *digest;
@property (nonatomic, readonly) CGSize mediaSize;
// Non-nil for attachments which need "lazy backup restore."
- (nullable OWSBackupFragment *)lazyRestoreFragment;
@ -49,7 +51,8 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
sourceFilename:(nullable NSString *)sourceFilename
caption:(nullable NSString *)caption
albumMessageId:(nullable NSString *)albumMessageId
attachmentType:(TSAttachmentType)attachmentType NS_DESIGNATED_INITIALIZER;
attachmentType:(TSAttachmentType)attachmentType
mediaSize:(CGSize)mediaSize NS_DESIGNATED_INITIALIZER;
- (instancetype)initForRestoreWithAttachmentStream:(TSAttachmentStream *)attachmentStream NS_DESIGNATED_INITIALIZER;

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "TSAttachmentPointer.h"
@ -12,6 +12,15 @@
NS_ASSUME_NONNULL_BEGIN
@interface TSAttachmentStream (TSAttachmentPointer)
- (CGSize)cachedMediaSize;
@end
#pragma mark -
@interface TSAttachmentPointer ()
// Optional property. Only set for attachments which need "lazy backup restore."
@ -53,6 +62,7 @@ NS_ASSUME_NONNULL_BEGIN
caption:(nullable NSString *)caption
albumMessageId:(nullable NSString *)albumMessageId
attachmentType:(TSAttachmentType)attachmentType
mediaSize:(CGSize)mediaSize
{
self = [super initWithServerId:serverId
encryptionKey:key
@ -69,6 +79,7 @@ NS_ASSUME_NONNULL_BEGIN
_state = TSAttachmentPointerStateEnqueued;
self.attachmentType = attachmentType;
_pointerType = TSAttachmentPointerTypeIncoming;
_mediaSize = mediaSize;
return self;
}
@ -89,6 +100,7 @@ NS_ASSUME_NONNULL_BEGIN
_state = TSAttachmentPointerStateEnqueued;
self.attachmentType = attachmentStream.attachmentType;
_pointerType = TSAttachmentPointerTypeRestoring;
_mediaSize = (attachmentStream.shouldHaveImageSize ? attachmentStream.cachedMediaSize : CGSizeZero);
return self;
}
@ -104,9 +116,19 @@ NS_ASSUME_NONNULL_BEGIN
OWSFailDebug(@"Invalid attachment key.");
return nil;
}
if (attachmentProto.contentType.length < 1) {
OWSFailDebug(@"Invalid attachment content type.");
return nil;
NSString *_Nullable fileName = attachmentProto.fileName;
NSString *_Nullable contentType = attachmentProto.contentType;
if (contentType.length < 1) {
// Content type might not set if the sending client can't
// infer a MIME type from the file extension.
OWSLogWarn(@"Invalid attachment content type.");
NSString *_Nullable fileExtension = [fileName pathExtension].lowercaseString;
if (fileExtension.length > 0) {
contentType = [MIMETypeUtil mimeTypeForFileExtension:fileExtension];
}
if (contentType.length < 1) {
contentType = OWSMimeTypeApplicationOctetStream;
}
}
// digest will be empty for old clients.
@ -129,15 +151,22 @@ NS_ASSUME_NONNULL_BEGIN
albumMessageId = albumMessage.uniqueId;
}
CGSize mediaSize = CGSizeZero;
if (attachmentProto.hasWidth && attachmentProto.hasHeight && attachmentProto.width > 0
&& attachmentProto.height > 0) {
mediaSize = CGSizeMake(attachmentProto.width, attachmentProto.height);
}
TSAttachmentPointer *pointer = [[TSAttachmentPointer alloc] initWithServerId:attachmentProto.id
key:attachmentProto.key
digest:digest
byteCount:attachmentProto.size
contentType:attachmentProto.contentType
sourceFilename:attachmentProto.fileName
contentType:contentType
sourceFilename:fileName
caption:caption
albumMessageId:albumMessageId
attachmentType:attachmentType];
attachmentType:attachmentType
mediaSize:mediaSize];
return pointer;
}

@ -526,6 +526,19 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
}
}
- (CGSize)cachedMediaSize
{
OWSAssertDebug(self.shouldHaveImageSize);
@synchronized(self) {
if (self.cachedImageWidth && self.cachedImageHeight) {
return CGSizeMake(self.cachedImageWidth.floatValue, self.cachedImageHeight.floatValue);
} else {
return CGSizeZero;
}
}
}
#pragma mark - Update With...
- (void)applyChangeAsyncToLatestCopyWithChangeBlock:(void (^)(TSAttachmentStream *))changeBlock

Loading…
Cancel
Save