Merge branch 'charlesmchen/albumProgress'

pull/1/head
Matthew Chen 7 years ago
commit 1b49fa294f

@ -1 +1 @@
Subproject commit 40ba5afa3249a18770c2340896fa28fdb662d188
Subproject commit cc810a1d99add579b5d8f099f06b543677bfeade

@ -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 = "<group>"; };
34E88D252098C5AE00A608F4 /* ContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = "<group>"; };
34E8A8D02085238900B272B1 /* ProtoParsingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoParsingTest.m; sourceTree = "<group>"; };
34EA693F2194933900702471 /* AttachmentDownloadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentDownloadView.swift; sourceTree = "<group>"; };
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = "<group>"; };
34F308A11ECB469700BB7697 /* OWSBezierPathView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBezierPathView.m; sourceTree = "<group>"; };
34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = "<group>"; };
@ -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 */,

@ -7,6 +7,7 @@
// Separate iOS Frameworks from other imports.
#import "AppSettingsViewController.h"
#import "AttachmentUploadView.h"
#import "ContactCellView.h"
#import "ContactTableViewCell.h"
#import "ConversationViewCell.h"

@ -429,7 +429,7 @@ private class MockConversationViewItem: NSObject, ConversationViewItem {
return 2
}
func firstValidGalleryAttachment() -> TSAttachmentStream? {
func firstValidAlbumAttachment() -> TSAttachmentStream? {
owsFailDebug("unexpected invocation")
return nil
}

@ -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()
}
}

@ -6,17 +6,32 @@ import Foundation
@objc
public class ConversationMediaView: UIView {
// MARK: - Dependencies
private var attachmentDownloads: OWSAttachmentDownloads {
return SSKEnvironment.shared.attachmentDownloads
}
// MARK: -
private let mediaCache: NSCache<NSString, AnyObject>
private let attachment: TSAttachment
private let isOutgoing: Bool
private let maxMessageWidth: CGFloat
private var loadBlock : (() -> Void)?
private var unloadBlock : (() -> Void)?
private var didFailToLoad = false
@objc
public required init(mediaCache: NSCache<NSString, AnyObject>,
attachment: TSAttachment) {
attachment: TSAttachment,
isOutgoing: Bool,
maxMessageWidth: CGFloat) {
self.mediaCache = mediaCache
self.attachment = attachment
self.isOutgoing = isOutgoing
self.maxMessageWidth = maxMessageWidth
super.init(frame: .zero)
@ -35,8 +50,7 @@ public class ConversationMediaView: UIView {
AssertIsOnMainThread()
guard let attachmentStream = attachment as? TSAttachmentStream else {
// TODO: Handle this case.
owsFailDebug("Missing attachment stream.")
addDownloadProgressIfNecessary()
return
}
if attachmentStream.isAnimated {
@ -48,13 +62,55 @@ public class ConversationMediaView: UIView {
} else {
// TODO: Handle this case.
owsFailDebug("Attachment has unexpected type.")
configureForMissingOrInvalid()
}
}
private func addMediaSubview(_ subview: UIView) {
addSubview(subview)
subview.autoPinEdgesToSuperviewEdges()
// TODO: Possibly add upload/download indicator here.
//
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 nil != 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 addUploadProgressIfNecessary(_ subview: UIView,
progressCallback: ProgressCallback? = nil) {
guard isOutgoing else {
return
}
guard let attachmentStream = attachment as? TSAttachmentStream else {
return
}
guard !attachmentStream.isUploaded else {
return
}
let uploadView = AttachmentUploadView(attachment: attachmentStream) { (isAttachmentReady) in
if let progressCallback = progressCallback {
progressCallback(isAttachmentReady)
}
}
subview.addSubview(uploadView)
uploadView.autoPinEdgesToSuperviewEdges()
}
private func configureForAnimatedImage(attachmentStream: TSAttachmentStream) {
@ -71,7 +127,10 @@ public class ConversationMediaView: UIView {
animatedImageView.layer.minificationFilter = kCAFilterTrilinear
animatedImageView.layer.magnificationFilter = kCAFilterTrilinear
animatedImageView.backgroundColor = Theme.offBackgroundColor
addMediaSubview(animatedImageView)
addSubview(animatedImageView)
animatedImageView.autoPinEdgesToSuperviewEdges()
addUploadProgressIfNecessary(animatedImageView)
loadBlock = { [weak self] in
guard let strongSelf = self else {
return
@ -91,8 +150,8 @@ public class ConversationMediaView: UIView {
let animatedImage = YYImage(contentsOfFile: filePath)
return animatedImage
},
cacheKey: cacheKey,
canLoadAsync: true)
cacheKey: cacheKey,
canLoadAsync: true)
guard let image = cachedValue as? YYImage else {
return
}
@ -117,7 +176,9 @@ public class ConversationMediaView: UIView {
stillImageView.layer.minificationFilter = kCAFilterTrilinear
stillImageView.layer.magnificationFilter = kCAFilterTrilinear
stillImageView.backgroundColor = Theme.offBackgroundColor
addMediaSubview(stillImageView)
addSubview(stillImageView)
stillImageView.autoPinEdgesToSuperviewEdges()
addUploadProgressIfNecessary(stillImageView)
loadBlock = { [weak self] in
guard let strongSelf = self else {
return
@ -136,8 +197,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
}
@ -162,14 +223,18 @@ public class ConversationMediaView: UIView {
stillImageView.layer.minificationFilter = kCAFilterTrilinear
stillImageView.layer.magnificationFilter = kCAFilterTrilinear
stillImageView.backgroundColor = Theme.offBackgroundColor
addMediaSubview(stillImageView)
// TODO: Hide during upload/download.
let videoPlayIcon = UIImage(named: "play_button")
let videoPlayButton = UIImageView(image: videoPlayIcon)
stillImageView.addSubview(videoPlayButton)
videoPlayButton.autoCenterInSuperview()
addSubview(stillImageView)
stillImageView.autoPinEdgesToSuperviewEdges()
addUploadProgressIfNecessary(stillImageView) { (isAttachmentReady) in
videoPlayButton.isHidden = !isAttachmentReady
}
loadBlock = { [weak self] in
guard let strongSelf = self else {
return
@ -188,8 +253,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
}
@ -200,6 +265,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? {

@ -22,11 +22,14 @@ public class MediaAlbumCellView: UIStackView {
@objc
public required init(mediaCache: NSCache<NSString, AnyObject>,
items: [ConversationMediaAlbumItem],
isOutgoing: Bool,
maxMessageWidth: CGFloat) {
self.items = items
self.itemViews = MediaAlbumCellView.itemsToDisplay(forItems: items).map {
ConversationMediaView(mediaCache: mediaCache,
attachment: $0.attachment)
attachment: $0.attachment,
isOutgoing: isOutgoing,
maxMessageWidth: maxMessageWidth)
}
super.init(frame: .zero)

@ -241,7 +241,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
case OWSMessageCellType_ContactShare:
return NO;
case OWSMessageCellType_MediaAlbum:
// Is there a gallery title?
// Is there an album title?
return self.hasBodyText;
}
}
@ -787,6 +787,7 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
OWSMediaAlbumCellView *albumView =
[[OWSMediaAlbumCellView alloc] initWithMediaCache:self.cellMediaCache
items:self.viewItem.mediaAlbumItems
isOutgoing:self.isOutgoing
maxMessageWidth:self.conversationStyle.maxMessageWidth];
self.loadCellContentBlock = ^{
[albumView loadMedia];
@ -804,7 +805,10 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
OWSAssertDebug([self.attachmentStream isVisualMedia]);
ConversationMediaView *mediaView =
[[ConversationMediaView alloc] initWithMediaCache:self.cellMediaCache attachment:self.attachmentStream];
[[ConversationMediaView alloc] initWithMediaCache:self.cellMediaCache
attachment:self.attachmentStream
isOutgoing:self.isOutgoing
maxMessageWidth:self.conversationStyle.maxMessageWidth];
self.loadCellContentBlock = ^{
[mediaView loadMedia];
};
@ -1414,9 +1418,9 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
OWSAssertDebug(self.viewItem.mediaAlbumItems.count > 0);
// For now, use first valid attachment.
TSAttachmentStream *_Nullable attachmentStream = self.viewItem.firstValidGalleryAttachment;
TSAttachmentStream *_Nullable attachmentStream = self.viewItem.firstValidAlbumAttachment;
if (!attachmentStream) {
OWSLogInfo(@"Ignoring tap on gallery without any valid attachments.");
OWSLogInfo(@"Ignoring tap on album without any valid attachments.");
return;
}

@ -144,7 +144,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
// For other view views (like the typing indicator), this is a unique, stable string.
- (NSString *)itemId;
- (nullable TSAttachmentStream *)firstValidGalleryAttachment;
- (nullable TSAttachmentStream *)firstValidAlbumAttachment;
@end

@ -409,7 +409,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
}
- (nullable TSAttachmentStream *)firstValidGalleryAttachment
- (nullable TSAttachmentStream *)firstValidAlbumAttachment
{
OWSAssertDebug(self.mediaAlbumItems.count > 0);
@ -575,9 +575,9 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
NSArray<ConversationMediaAlbumItem *> *mediaAlbumItems = [self mediaAlbumItemsForAttachments:attachments];
self.mediaAlbumItems = mediaAlbumItems;
self.messageCellType = OWSMessageCellType_MediaAlbum;
NSString *_Nullable galleryTitle = [message bodyTextWithTransaction:transaction];
if (galleryTitle) {
self.displayableBodyText = [self displayableBodyTextForText:galleryTitle interactionId:message.uniqueId];
NSString *_Nullable albumTitle = [message bodyTextWithTransaction:transaction];
if (albumTitle) {
self.displayableBodyText = [self displayableBodyTextForText:albumTitle interactionId:message.uniqueId];
}
return;
}
@ -899,7 +899,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
break;
}
case OWSMessageCellType_MediaAlbum: {
OWSFailDebug(@"Can't copy media gallery");
OWSFailDebug(@"Can't copy media album");
break;
}
}
@ -952,7 +952,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
}
if (attachmentStreams.count < 1) {
OWSFailDebug(@"Can't share media gallery; no valid items.");
OWSFailDebug(@"Can't share media album; no valid items.");
return;
}
[AttachmentSharing showShareUIForAttachments:attachmentStreams completion:nil];
@ -1126,7 +1126,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
case OWSMessageCellType_MediaAlbum:
// TODO: I suspect we need separate "can save media", "can share media", etc. methods.
return self.firstValidGalleryAttachment != nil;
return self.firstValidAlbumAttachment != nil;
}
}

@ -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.

@ -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<NSString *> *downloadingAttachmentIdSet;
@property (nonatomic, readonly) NSMutableDictionary<NSString *, OWSAttachmentDownloadJob *> *downloadingJobMap;
// This property should only be accessed while synchronized on this class.
@property (nonatomic, readonly) NSMutableArray<OWSAttachmentDownloadJob *> *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<TSAttachmentStream *> *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) {

Loading…
Cancel
Save