From 654325c6dcc61a75c4debd7ceb73c3bda47a00de Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 8 Nov 2018 11:14:30 -0500 Subject: [PATCH] Add download progress indicators. --- Signal.xcodeproj/project.pbxproj | 4 + .../Cells/AttachmentDownloadView.swift | 84 ++++++++++++++++++ .../Cells/ConversationMediaView.swift | 66 ++++++++++++-- .../Cells/MediaAlbumCellView.swift | 3 +- .../Cells/OWSMessageBubbleView.m | 8 +- .../Attachments/OWSAttachmentDownloads.h | 2 + .../Attachments/OWSAttachmentDownloads.m | 86 ++++++++++++------- 7 files changed, 209 insertions(+), 44 deletions(-) create mode 100644 Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index e85b8eae9..b2e3f6447 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -283,6 +283,7 @@ 34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */; }; 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E88D252098C5AE00A608F4 /* ContactViewController.swift */; }; 34E8A8D12085238A00B272B1 /* ProtoParsingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */; }; + 34EA69402194933900702471 /* AttachmentDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA693F2194933900702471 /* AttachmentDownloadView.swift */; }; 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; }; 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; }; 4503F1BE20470A5B00CEE724 /* classic-quiet.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 4503F1BB20470A5B00CEE724 /* classic-quiet.aifc */; }; @@ -981,6 +982,7 @@ 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegistrationUtils.m; sourceTree = ""; }; 34E88D252098C5AE00A608F4 /* ContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = ""; }; 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoParsingTest.m; sourceTree = ""; }; + 34EA693F2194933900702471 /* AttachmentDownloadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentDownloadView.swift; sourceTree = ""; }; 34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = ""; }; 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBezierPathView.m; sourceTree = ""; }; 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = ""; }; @@ -1828,6 +1830,7 @@ 34D1F0951F867BFC0066283D /* Cells */ = { isa = PBXGroup; children = ( + 34EA693F2194933900702471 /* AttachmentDownloadView.swift */, 34D1F0BB1F8D108C0066283D /* AttachmentUploadView.h */, 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */, 3488F9352191CC4000E524CC /* ConversationMediaView.swift */, @@ -3419,6 +3422,7 @@ 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, + 34EA69402194933900702471 /* AttachmentDownloadView.swift in Sources */, 340FC8AB204DAC8D007AEB0F /* DomainFrontingCountryViewController.m in Sources */, 3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */, 34B3F8751E8DF1700035BE1A /* CallViewController.swift in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift b/Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift new file mode 100644 index 000000000..1915d6939 --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift @@ -0,0 +1,84 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +public class AttachmentDownloadView: UIView { + + // MARK: - Dependencies + + private var attachmentDownloads: OWSAttachmentDownloads { + return SSKEnvironment.shared.attachmentDownloads + } + + // MARK: - + + private let attachmentId: String + private let radius: CGFloat + private let shapeLayer = CAShapeLayer() + + @objc + public required init(attachmentId: String, radius: CGFloat) { + self.attachmentId = attachmentId + self.radius = radius + + super.init(frame: .zero) + + layer.addSublayer(shapeLayer) + } + + @available(*, unavailable, message: "use other init() instead.") + required public init?(coder aDecoder: NSCoder) { + notImplemented() + } + + @objc public override var bounds: CGRect { + didSet { + if oldValue != bounds { + updateLayers() + } + } + } + + @objc public override var frame: CGRect { + didSet { + if oldValue != frame { + updateLayers() + } + } + } + + internal func updateLayers() { + AssertIsOnMainThread() + + self.shapeLayer.frame = self.bounds + + guard let progress = attachmentDownloads.downloadProgress(forAttachmentId: attachmentId) else { + Logger.warn("No progress for attachment.") + shapeLayer.path = nil + return + } + + // Prevent the shape layer from animating changes. + CATransaction.begin() + CATransaction.setDisableActions(true) + + let center = CGPoint(x: self.bounds.width * 0.5, + y: self.bounds.height * 0.5) + let outerRadius: CGFloat = radius * 1.0 + let innerRadius: CGFloat = radius * 0.9 + let startAngle: CGFloat = CGFloat.pi * 1.5 + let endAngle: CGFloat = CGFloat.pi * (1.5 + 2 * CGFloat(progress.floatValue)) + + let bezierPath = UIBezierPath() + bezierPath.addArc(withCenter: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) + bezierPath.addArc(withCenter: center, radius: innerRadius, startAngle: endAngle, endAngle: startAngle, clockwise: false) + + shapeLayer.path = bezierPath.cgPath + shapeLayer.fillColor = UIColor.ows_gray60.cgColor + + CATransaction.commit() + } +} diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift index ec3146743..eb9074375 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift @@ -6,9 +6,19 @@ import Foundation @objc public class ConversationMediaView: UIView { + + // MARK: - Dependencies + + private var attachmentDownloads: OWSAttachmentDownloads { + return SSKEnvironment.shared.attachmentDownloads + } + + // MARK: - + private let mediaCache: NSCache private let attachment: TSAttachment private let isOutgoing: Bool + private let maxMessageWidth: CGFloat private var loadBlock : (() -> Void)? private var unloadBlock : (() -> Void)? private var didFailToLoad = false @@ -16,10 +26,12 @@ public class ConversationMediaView: UIView { @objc public required init(mediaCache: NSCache, attachment: TSAttachment, - isOutgoing: Bool) { + isOutgoing: Bool, + maxMessageWidth: CGFloat) { self.mediaCache = mediaCache self.attachment = attachment self.isOutgoing = isOutgoing + self.maxMessageWidth = maxMessageWidth super.init(frame: .zero) @@ -38,6 +50,7 @@ public class ConversationMediaView: UIView { AssertIsOnMainThread() guard let attachmentStream = attachment as? TSAttachmentStream else { + addDownloadProgressIfNecessary() return } if attachmentStream.isAnimated { @@ -49,11 +62,39 @@ public class ConversationMediaView: UIView { } else { // TODO: Handle this case. owsFailDebug("Attachment has unexpected type.") + configureForMissingOrInvalid() + } + } + + // + typealias ProgressCallback = (Bool) -> Void + + private func addDownloadProgressIfNecessary() { + guard let attachmentPointer = attachment as? TSAttachmentPointer else { + owsFailDebug("Attachment has unexpected type.") + configureForMissingOrInvalid() + return } + guard let attachmentId = attachmentPointer.uniqueId else { + owsFailDebug("Attachment stream missing unique ID.") + configureForMissingOrInvalid() + return + } + + guard let progress = attachmentDownloads.downloadProgress(forAttachmentId: attachmentId) else { + // Not being downloaded. + configureForMissingOrInvalid() + return + } + + backgroundColor = UIColor.ows_gray05 + let progressView = AttachmentDownloadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) + self.addSubview(progressView) + progressView.autoPinEdgesToSuperviewEdges() } - private func addAttachmentUploadViewIfNecessary(_ subview: UIView, - completion: @escaping (Bool) -> Void) { + private func addUploadProgressIfNecessary(_ subview: UIView, + progressCallback: @escaping ProgressCallback) { guard isOutgoing else { return } @@ -63,7 +104,8 @@ public class ConversationMediaView: UIView { guard !attachmentStream.isUploaded else { return } - let uploadView = AttachmentUploadView(attachment: attachmentStream) { (_) in + let uploadView = AttachmentUploadView(attachment: attachmentStream) { (isAttachmentReady) in + progressCallback(isAttachmentReady) } subview.addSubview(uploadView) uploadView.autoPinEdgesToSuperviewEdges() @@ -85,7 +127,7 @@ public class ConversationMediaView: UIView { animatedImageView.backgroundColor = Theme.offBackgroundColor addSubview(animatedImageView) animatedImageView.autoPinEdgesToSuperviewEdges() - addAttachmentUploadViewIfNecessary(animatedImageView) { (_) in + addUploadProgressIfNecessary(animatedImageView) { (_) in } loadBlock = { [weak self] in guard let strongSelf = self else { @@ -134,7 +176,7 @@ public class ConversationMediaView: UIView { stillImageView.backgroundColor = Theme.offBackgroundColor addSubview(stillImageView) stillImageView.autoPinEdgesToSuperviewEdges() - addAttachmentUploadViewIfNecessary(stillImageView) { (_) in + addUploadProgressIfNecessary(stillImageView) { (_) in } loadBlock = { [weak self] in guard let strongSelf = self else { @@ -188,7 +230,7 @@ public class ConversationMediaView: UIView { addSubview(stillImageView) stillImageView.autoPinEdgesToSuperviewEdges() - addAttachmentUploadViewIfNecessary(stillImageView) { (isAttachmentReady) in + addUploadProgressIfNecessary(stillImageView) { (isAttachmentReady) in videoPlayButton.isHidden = !isAttachmentReady } @@ -210,8 +252,8 @@ public class ConversationMediaView: UIView { Logger.error("Could not load thumbnail") }) }, - cacheKey: cacheKey, - canLoadAsync: true) + cacheKey: cacheKey, + canLoadAsync: true) guard let image = cachedValue as? UIImage else { return } @@ -222,6 +264,12 @@ public class ConversationMediaView: UIView { } } + private func configureForMissingOrInvalid() { + // TODO: Get final value from design. + backgroundColor = UIColor.ows_gray45 + // TODO: Add error icon. + } + private func tryToLoadMedia(loadMediaBlock: @escaping () -> AnyObject?, cacheKey: String, canLoadAsync: Bool) -> AnyObject? { diff --git a/Signal/src/ViewControllers/ConversationView/Cells/MediaAlbumCellView.swift b/Signal/src/ViewControllers/ConversationView/Cells/MediaAlbumCellView.swift index 8bcf444c4..a107f1d46 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/MediaAlbumCellView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/MediaAlbumCellView.swift @@ -28,7 +28,8 @@ public class MediaAlbumCellView: UIStackView { self.itemViews = MediaAlbumCellView.itemsToDisplay(forItems: items).map { ConversationMediaView(mediaCache: mediaCache, attachment: $0.attachment, - isOutgoing: isOutgoing) + isOutgoing: isOutgoing, + maxMessageWidth: maxMessageWidth) } super.init(frame: .zero) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index c787dc287..fd4eff6b6 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -804,9 +804,11 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes OWSAssertDebug(self.attachmentStream); OWSAssertDebug([self.attachmentStream isVisualMedia]); - ConversationMediaView *mediaView = [[ConversationMediaView alloc] initWithMediaCache:self.cellMediaCache - attachment:self.attachmentStream - isOutgoing:self.isOutgoing]; + ConversationMediaView *mediaView = + [[ConversationMediaView alloc] initWithMediaCache:self.cellMediaCache + attachment:self.attachmentStream + isOutgoing:self.isOutgoing + maxMessageWidth:self.conversationStyle.maxMessageWidth]; self.loadCellContentBlock = ^{ [mediaView loadMedia]; }; diff --git a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.h b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.h index e08e60c82..1701e9296 100644 --- a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.h +++ b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.h @@ -24,6 +24,8 @@ extern NSString *const kAttachmentDownloadAttachmentIDKey; */ @interface OWSAttachmentDownloads : NSObject +- (nullable NSNumber *)downloadProgressForAttachmentId:(NSString *)attachmentId; + // This will try to download all un-downloaded attachments for a given message. // Any attachments for the message which are already downloaded are skipped BUT // they are included in the success callback. diff --git a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.m b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.m index 9014dd2ca..a5d3d3a94 100644 --- a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.m +++ b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentDownloads.m @@ -45,6 +45,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); @property (nonatomic, readonly, nullable) TSMessage *message; @property (nonatomic, readonly) AttachmentDownloadSuccess success; @property (nonatomic, readonly) AttachmentDownloadFailure failure; +@property (atomic) CGFloat progress; @end @@ -77,7 +78,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); @interface OWSAttachmentDownloads () // This property should only be accessed while synchronized on this class. -@property (nonatomic, readonly) NSMutableSet *downloadingAttachmentIdSet; +@property (nonatomic, readonly) NSMutableDictionary *downloadingJobMap; // This property should only be accessed while synchronized on this class. @property (nonatomic, readonly) NSMutableArray *attachmentDownloadJobQueue; @@ -109,7 +110,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); return self; } - _downloadingAttachmentIdSet = [NSMutableSet new]; + _downloadingJobMap = [NSMutableDictionary new]; _attachmentDownloadJobQueue = [NSMutableArray new]; return self; @@ -117,6 +118,18 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); #pragma mark - +- (nullable NSNumber *)downloadProgressForAttachmentId:(NSString *)attachmentId +{ + + @synchronized(self) { + OWSAttachmentDownloadJob *_Nullable job = self.downloadingJobMap[attachmentId]; + if (!job) { + return nil; + } + return @(job.progress); + } +} + - (void)downloadAttachmentsForMessage:(TSMessage *)message transaction:(YapDatabaseReadTransaction *)transaction success:(void (^)(NSArray *attachmentStreams))success @@ -253,20 +266,20 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); @synchronized(self) { const NSUInteger kMaxSimultaneousDownloads = 4; - if (self.downloadingAttachmentIdSet.count >= kMaxSimultaneousDownloads) { + if (self.downloadingJobMap.count >= kMaxSimultaneousDownloads) { return; } job = self.attachmentDownloadJobQueue.firstObject; if (!job) { return; } - if ([self.downloadingAttachmentIdSet containsObject:job.attachmentPointer.uniqueId]) { + if (self.downloadingJobMap[job.attachmentPointer.uniqueId] != nil) { // Ensure we only have one download in flight at a time for a given attachment. OWSLogWarn(@"Ignoring duplicate download."); return; } [self.attachmentDownloadJobQueue removeObjectAtIndex:0]; - [self.downloadingAttachmentIdSet addObject:job.attachmentPointer.uniqueId]; + self.downloadingJobMap[job.attachmentPointer.uniqueId] = job; } [self.primaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { @@ -278,7 +291,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); } }]; - [self retrieveAttachment:job.attachmentPointer + [self retrieveAttachmentForJob:job success:^(TSAttachmentStream *attachmentStream) { OWSLogVerbose(@"Attachment download succeeded."); @@ -294,7 +307,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); job.success(attachmentStream); @synchronized(self) { - [self.downloadingAttachmentIdSet removeObject:job.attachmentPointer.uniqueId]; + [self.downloadingJobMap removeObjectForKey:job.attachmentPointer.uniqueId]; } [self startDownloadIfPossible]; @@ -314,7 +327,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); }]; @synchronized(self) { - [self.downloadingAttachmentIdSet removeObject:job.attachmentPointer.uniqueId]; + [self.downloadingJobMap removeObjectForKey:job.attachmentPointer.uniqueId]; } job.failure(error); @@ -326,11 +339,12 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); #pragma mark - -- (void)retrieveAttachment:(TSAttachmentPointer *)attachment - success:(void (^)(TSAttachmentStream *attachmentStream))successHandler - failure:(void (^)(NSError *error))failureHandler +- (void)retrieveAttachmentForJob:(OWSAttachmentDownloadJob *)job + success:(void (^)(TSAttachmentStream *attachmentStream))successHandler + failure:(void (^)(NSError *error))failureHandler { - OWSAssertDebug(attachment); + OWSAssertDebug(job); + TSAttachmentPointer *attachmentPointer = job.attachmentPointer; __block OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; @@ -353,10 +367,10 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); }); }; - if (attachment.serverId < 100) { - OWSLogError(@"Suspicious attachment id: %llu", (unsigned long long)attachment.serverId); + if (attachmentPointer.serverId < 100) { + OWSLogError(@"Suspicious attachment id: %llu", (unsigned long long)attachmentPointer.serverId); } - TSRequest *request = [OWSRequestFactory attachmentRequestWithAttachmentId:attachment.serverId]; + TSRequest *request = [OWSRequestFactory attachmentRequestWithAttachmentId:attachmentPointer.serverId]; [self.networkManager makeRequest:request success:^(NSURLSessionDataTask *task, id responseObject) { @@ -374,22 +388,22 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); dispatch_async([OWSDispatch attachmentsQueue], ^{ [self downloadFromLocation:location - pointer:attachment + job:job success:^(NSString *encryptedDataFilePath) { [self decryptAttachmentPath:encryptedDataFilePath - pointer:attachment + attachmentPointer:attachmentPointer success:markAndHandleSuccess failure:markAndHandleFailure]; } failure:^(NSURLSessionTask *_Nullable task, NSError *error) { - if (attachment.serverId < 100) { + if (attachmentPointer.serverId < 100) { // This looks like the symptom of the "frequent 404 // downloading attachments with low server ids". NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response; NSInteger statusCode = [httpResponse statusCode]; OWSFailDebug(@"%d Failure with suspicious attachment id: %llu, %@", (int)statusCode, - (unsigned long long)attachment.serverId, + (unsigned long long)attachmentPointer.serverId, error); } markAndHandleFailure(error); @@ -401,14 +415,14 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); OWSProdError([OWSAnalyticsEvents errorAttachmentRequestFailed]); } OWSLogError(@"Failed retrieval of attachment with error: %@", error); - if (attachment.serverId < 100) { + if (attachmentPointer.serverId < 100) { // This _shouldn't_ be the symptom of the "frequent 404 // downloading attachments with low server ids". NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response; NSInteger statusCode = [httpResponse statusCode]; OWSFailDebug(@"%d Failure with suspicious attachment id: %llu, %@", (int)statusCode, - (unsigned long long)attachment.serverId, + (unsigned long long)attachmentPointer.serverId, error); } return markAndHandleFailure(error); @@ -416,12 +430,12 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); } - (void)decryptAttachmentPath:(NSString *)encryptedDataFilePath - pointer:(TSAttachmentPointer *)attachment + attachmentPointer:(TSAttachmentPointer *)attachmentPointer success:(void (^)(TSAttachmentStream *attachmentStream))success failure:(void (^)(NSError *error))failure { OWSAssertDebug(encryptedDataFilePath.length > 0); - OWSAssertDebug(attachment); + OWSAssertDebug(attachmentPointer); // Use attachmentDecryptSerialQueue to ensure that we only load into memory // & decrypt a single attachment at a time. @@ -435,7 +449,10 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); return failure(error); } - [self decryptAttachmentData:encryptedData pointer:attachment success:success failure:failure]; + [self decryptAttachmentData:encryptedData + attachmentPointer:attachmentPointer + success:success + failure:failure]; if (![OWSFileSystem deleteFile:encryptedDataFilePath]) { OWSLogError(@"Could not delete temporary file."); @@ -445,15 +462,17 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); } - (void)decryptAttachmentData:(NSData *)cipherText - pointer:(TSAttachmentPointer *)attachment + attachmentPointer:(TSAttachmentPointer *)attachmentPointer success:(void (^)(TSAttachmentStream *attachmentStream))successHandler failure:(void (^)(NSError *error))failureHandler { + OWSAssertDebug(attachmentPointer); + NSError *decryptError; NSData *_Nullable plaintext = [Cryptography decryptAttachment:cipherText - withKey:attachment.encryptionKey - digest:attachment.digest - unpaddedSize:attachment.byteCount + withKey:attachmentPointer.encryptionKey + digest:attachmentPointer.digest + unpaddedSize:attachmentPointer.byteCount error:&decryptError]; if (decryptError) { @@ -469,7 +488,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); return; } - TSAttachmentStream *stream = [[TSAttachmentStream alloc] initWithPointer:attachment]; + TSAttachmentStream *stream = [[TSAttachmentStream alloc] initWithPointer:attachmentPointer]; NSError *writeError; [stream writeData:plaintext error:&writeError]; @@ -494,10 +513,13 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); } - (void)downloadFromLocation:(NSString *)location - pointer:(TSAttachmentPointer *)pointer + job:(OWSAttachmentDownloadJob *)job success:(void (^)(NSString *encryptedDataPath))successHandler failure:(void (^)(NSURLSessionTask *_Nullable task, NSError *error))failureHandlerParam { + OWSAssertDebug(job); + TSAttachmentPointer *attachmentPointer = job.attachmentPointer; + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.requestSerializer = [AFHTTPRequestSerializer serializer]; [manager.requestSerializer setValue:OWSMimeTypeApplicationOctetStream forHTTPHeaderField:@"Content-Type"]; @@ -560,8 +582,10 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); return; } + job.progress = progress.fractionCompleted; + [self fireProgressNotification:MAX(kAttachmentDownloadProgressTheta, progress.fractionCompleted) - attachmentId:pointer.uniqueId]; + attachmentId:attachmentPointer.uniqueId]; // We only need to check the content length header once. if (hasCheckedContentLength) {