Add failure methods to thumbnail service.

pull/1/head
Matthew Chen 7 years ago
parent f6e792c707
commit 206432fdf0

@ -859,9 +859,13 @@ NS_ASSUME_NONNULL_BEGIN
stillImageView.image = [strongSelf
tryToLoadCellMedia:^{
OWSCAssert([strongSelf.attachmentStream isImage]);
return [strongSelf.attachmentStream thumbnailImageMediumWithCompletion:^(UIImage *image) {
weakImageView.image = image;
}];
return [strongSelf.attachmentStream
thumbnailImageMediumWithSuccess:^(UIImage *image) {
weakImageView.image = image;
}
failure:^{
DDLogError(@"Could not load thumbnail.");
}];
}
mediaView:stillImageView
cacheKey:strongSelf.attachmentStream.uniqueId

@ -35,7 +35,7 @@ public struct MediaGalleryItem: Equatable, Hashable {
public typealias AsyncThumbnailBlock = (UIImage) -> Void
func thumbnailImage(async:@escaping AsyncThumbnailBlock) -> UIImage? {
return attachmentStream.thumbnailImageSmall(completion: async)
return attachmentStream.thumbnailImageSmall(success: async, failure: {})
}
var fullSizedImage: UIImage {

@ -323,10 +323,8 @@ typedef void (^OrphanDataBlock)(OWSOrphanData *);
OWSFail(@"%@ attachment has no file path.", self.logTag);
}
NSString *_Nullable thumbnailPath = [attachmentStream legacyThumbnailPath];
if (thumbnailPath.length > 0) {
[allAttachmentFilePaths addObject:thumbnailPath];
}
[allAttachmentFilePaths
addObjectsFromArray:attachmentStream.allThumbnailPaths];
}];
if (shouldAbort) {

@ -227,7 +227,7 @@ NS_ASSUME_NONNULL_BEGIN
authorId:authorId
body:quotedText
bodySource:TSQuotedMessageContentSourceLocal
thumbnailImage:quotedAttachment.legacyThumbnailImage
thumbnailImage:quotedAttachment.thumbnailImageSmallSync
contentType:quotedAttachment.contentType
sourceFilename:quotedAttachment.sourceFilename
attachmentStream:quotedAttachment

@ -4,8 +4,33 @@
import Foundation
@objc public class OWSLoadedThumbnail: NSObject {
public typealias DataSourceBlock = () throws -> Data
@objc public let image: UIImage
let dataSourceBlock: DataSourceBlock
@objc public init(image: UIImage, filePath: String) {
self.image = image
self.dataSourceBlock = {
return try Data(contentsOf: URL(fileURLWithPath: filePath))
}
}
@objc public init(image: UIImage, data: Data) {
self.image = image
self.dataSourceBlock = {
return data
}
}
@objc public func data() throws -> Data {
return try dataSourceBlock()
}
}
private struct OWSThumbnailRequest {
public typealias SuccessBlock = (UIImage) -> Void
public typealias SuccessBlock = (OWSLoadedThumbnail) -> Void
public typealias FailureBlock = () -> Void
let attachmentId: String
@ -28,7 +53,7 @@ private struct OWSThumbnailRequest {
@objc(shared)
public static let shared = OWSThumbnailService()
public typealias SuccessBlock = (UIImage) -> Void
public typealias SuccessBlock = (OWSLoadedThumbnail) -> Void
public typealias FailureBlock = () -> Void
private let serialQueue = DispatchQueue(label: "OWSThumbnailService")
@ -97,9 +122,9 @@ private struct OWSThumbnailRequest {
}
let thumbnailRequest = thumbnailRequestStack.removeLast()
if let image = process(thumbnailRequest: thumbnailRequest) {
if let loadedThumbnail = process(thumbnailRequest: thumbnailRequest) {
DispatchQueue.main.async {
thumbnailRequest.success(image)
thumbnailRequest.success(loadedThumbnail)
}
} else {
DispatchQueue.main.async {
@ -109,7 +134,7 @@ private struct OWSThumbnailRequest {
}
// This should only be called on the serialQueue.
private func process(thumbnailRequest: OWSThumbnailRequest) -> UIImage? {
private func process(thumbnailRequest: OWSThumbnailRequest) -> OWSLoadedThumbnail? {
var possibleAttachment: TSAttachmentStream?
self.dbConnection.read({ (transaction) in
possibleAttachment = TSAttachmentStream.fetch(uniqueId: thumbnailRequest.attachmentId, transaction: transaction)
@ -133,7 +158,7 @@ private struct OWSThumbnailRequest {
owsFail("Could not load thumbnail.")
return nil
}
return image
return OWSLoadedThumbnail(image: image, filePath: filePath)
}
}
}
@ -200,6 +225,6 @@ private struct OWSThumbnailRequest {
size: thumbnailSize,
transaction: transaction)
})
return thumbnailImage
return OWSLoadedThumbnail(image: thumbnailImage, data: thumbnailData)
}
}

@ -17,7 +17,8 @@ NS_ASSUME_NONNULL_BEGIN
@class TSAttachmentPointer;
@class YapDatabaseReadWriteTransaction;
typedef void (^OWSThumbnailCompletion)(UIImage *image);
typedef void (^OWSThumbnailSuccess)(UIImage *image);
typedef void (^OWSThumbnailFailure)(void);
@interface TSAttachmentThumbnail : MTLModel
@ -66,10 +67,7 @@ typedef void (^OWSThumbnailCompletion)(UIImage *image);
- (nullable NSString *)originalFilePath;
- (nullable NSURL *)originalMediaURL;
// TODO: Rename to legacy...
- (nullable UIImage *)legacyThumbnailImage;
//- (nullable NSData *)legacyThumbnailData;
- (nullable NSString *)legacyThumbnailPath;
- (NSArray<NSString *> *)allThumbnailPaths;
+ (BOOL)hasThumbnailForMimeType:(NSString *)contentType;
@ -96,16 +94,18 @@ typedef void (^OWSThumbnailCompletion)(UIImage *image);
// Non-nil for attachments which need "lazy backup restore."
- (nullable OWSBackupFragment *)lazyRestoreFragment;
#pragma mark - Thumbnails
// On cache hit, the thumbnail will be returned synchronously and completion will never be invoked.
// On cache miss, nil will be returned and the completion will be invoked async on main if
// thumbnail can be generated.
- (nullable UIImage *)thumbnailImageWithSizeHint:(CGSize)sizeHint completion:(OWSThumbnailCompletion)completion;
- (nullable UIImage *)thumbnailImageSmallWithCompletion:(OWSThumbnailCompletion)completion;
- (nullable UIImage *)thumbnailImageMediumWithCompletion:(OWSThumbnailCompletion)completion;
- (nullable UIImage *)thumbnailImageLargeWithCompletion:(OWSThumbnailCompletion)completion;
- (nullable UIImage *)thumbnailImageWithSizeHint:(CGSize)sizeHint
completion:(OWSThumbnailSuccess)success
failure:(OWSThumbnailFailure)failure;
- (nullable UIImage *)thumbnailImageSmallWithSuccess:(OWSThumbnailSuccess)success failure:(OWSThumbnailFailure)failure;
- (nullable UIImage *)thumbnailImageMediumWithSuccess:(OWSThumbnailSuccess)success failure:(OWSThumbnailFailure)failure;
- (nullable UIImage *)thumbnailImageLargeWithSuccess:(OWSThumbnailSuccess)success failure:(OWSThumbnailFailure)failure;
- (nullable UIImage *)thumbnailImageSmallSync;
// This method should only be invoked by OWSThumbnailService.
- (nullable NSString *)pathForThumbnail:(TSAttachmentThumbnail *)thumbnail;

@ -24,6 +24,8 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
return MAX(screenSizePoints.width, screenSizePoints.height);
}
typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
@implementation TSAttachmentThumbnail
- (instancetype)initWithFilename:(NSString *)filename
@ -128,18 +130,9 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
_creationTimestamp = [NSDate new];
}
// This is going to be slow the first time it runs.
[self ensureLegacyThumbnail];
return self;
}
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[super saveWithTransaction:transaction];
[self ensureLegacyThumbnail];
}
- (void)upgradeFromAttachmentSchemaVersion:(NSUInteger)attachmentSchemaVersion
{
[super upgradeFromAttachmentSchemaVersion:attachmentSchemaVersion];
@ -429,24 +422,6 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
[MIMETypeUtil isAnimated:contentType]);
}
- (nullable NSData *)legacyThumbnailData
{
NSString *thumbnailPath = self.legacyThumbnailPath;
if (!thumbnailPath) {
OWSAssert(!self.isImage && !self.isVideo && !self.isAnimated);
return nil;
}
if (![[NSFileManager defaultManager] fileExistsAtPath:thumbnailPath]) {
OWSFail(@"%@ missing thumbnail for attachmentId: %@", self.logTag, self.uniqueId);
return nil;
}
return [NSData dataWithContentsOfFile:thumbnailPath];
}
- (void)ensureLegacyThumbnail
{
NSString *thumbnailPath = self.legacyThumbnailPath;
@ -764,7 +739,9 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
#pragma mark - Thumbnails
- (nullable UIImage *)thumbnailImageWithSizeHint:(CGSize)sizeHint completion:(OWSThumbnailCompletion)completion
- (nullable UIImage *)thumbnailImageWithSizeHint:(CGSize)sizeHint
success:(OWSThumbnailSuccess)success
failure:(OWSThumbnailFailure)failure
{
CGFloat maxDimensionHint = MAX(sizeHint.width, sizeHint.height);
NSUInteger thumbnailDimensionPoints;
@ -776,26 +753,46 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
thumbnailDimensionPoints = ThumbnailDimensionPointsLarge();
}
return [self thumbnailImageWithThumbnailDimensionPoints:thumbnailDimensionPoints completion:completion];
return [self thumbnailImageWithThumbnailDimensionPoints:thumbnailDimensionPoints success:success failure:failure];
}
- (nullable UIImage *)thumbnailImageSmallWithCompletion:(OWSThumbnailCompletion)completion
- (nullable UIImage *)thumbnailImageSmallWithSuccess:(OWSThumbnailSuccess)success failure:(OWSThumbnailFailure)failure
{
return [self thumbnailImageWithThumbnailDimensionPoints:kThumbnailDimensionPointsSmall completion:completion];
return [self thumbnailImageWithThumbnailDimensionPoints:kThumbnailDimensionPointsSmall
success:success
failure:failure];
}
- (nullable UIImage *)thumbnailImageMediumWithCompletion:(OWSThumbnailCompletion)completion
- (nullable UIImage *)thumbnailImageMediumWithSuccess:(OWSThumbnailSuccess)success failure:(OWSThumbnailFailure)failure
{
return [self thumbnailImageWithThumbnailDimensionPoints:kThumbnailDimensionPointsMedium completion:completion];
return [self thumbnailImageWithThumbnailDimensionPoints:kThumbnailDimensionPointsMedium
success:success
failure:failure];
}
- (nullable UIImage *)thumbnailImageLargeWithCompletion:(OWSThumbnailCompletion)completion
- (nullable UIImage *)thumbnailImageLargeWithSuccess:(OWSThumbnailSuccess)success failure:(OWSThumbnailFailure)failure
{
return [self thumbnailImageWithThumbnailDimensionPoints:ThumbnailDimensionPointsLarge() completion:completion];
return [self thumbnailImageWithThumbnailDimensionPoints:ThumbnailDimensionPointsLarge()
success:success
failure:failure];
}
- (nullable UIImage *)thumbnailImageWithThumbnailDimensionPoints:(NSUInteger)thumbnailDimensionPoints
completion:(OWSThumbnailCompletion)completion
success:(OWSThumbnailSuccess)success
failure:(OWSThumbnailFailure)failure
{
OWSLoadedThumbnail *_Nullable loadedThumbnail;
loadedThumbnail = [self loadedThumbnailWithThumbnailDimensionPoints:thumbnailDimensionPoints
success:^(OWSLoadedThumbnail *loadedThumbnail) {
success(loadedThumbnail.image);
}
failure:failure];
return loadedThumbnail.image;
}
- (nullable OWSLoadedThumbnail *)loadedThumbnailWithThumbnailDimensionPoints:(NSUInteger)thumbnailDimensionPoints
success:(OWSLoadedThumbnailSuccess)success
failure:(OWSThumbnailFailure)failure
{
CGSize originalSize = self.imageSize;
if (originalSize.width < 1 || originalSize.height < 1) {
@ -804,7 +801,7 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
if (originalSize.width <= thumbnailDimensionPoints || originalSize.height <= thumbnailDimensionPoints) {
// There's no point in generating a thumbnail if the original is smaller than the
// thumbnail size.
return self.originalImage;
return [[OWSLoadedThumbnail alloc] initWithImage:self.originalImage filePath:self.originalFilePath];
}
for (TSAttachmentThumbnail *thumbnail in self.thumbnails) {
@ -817,15 +814,77 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
continue;
}
UIImage *_Nullable image = [UIImage imageWithContentsOfFile:thumbnailPath];
return image;
if (!image) {
OWSFail(@"couldn't load image.");
continue;
}
return [[OWSLoadedThumbnail alloc] initWithImage:image filePath:thumbnailPath];
}
[OWSThumbnailService.shared ensureThumbnailForAttachmentIdWithAttachmentId:self.uniqueId
thumbnailDimensionPoints:thumbnailDimensionPoints
completion:completion];
success:success
failure:failure];
return nil;
}
- (nullable OWSLoadedThumbnail *)loadedThumbnailSmallSync
{
__block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block OWSLoadedThumbnail *_Nullable loadedThumbnail = nil;
loadedThumbnail = [self loadedThumbnailWithThumbnailDimensionPoints:kThumbnailDimensionPointsSmall
success:^(OWSLoadedThumbnail *asyncLoadedThumbnail) {
@synchronized(self) {
loadedThumbnail = asyncLoadedThumbnail;
}
dispatch_semaphore_signal(semaphore);
}
failure:^{
dispatch_semaphore_signal(semaphore);
}];
// Wait up to five seconds.
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)));
@synchronized(self) {
return loadedThumbnail;
}
}
- (nullable UIImage *)thumbnailImageSmallSync
{
return [self loadedThumbnailSmallSync].image;
}
- (nullable NSData *)thumbnailDataSmallSync
{
NSError *error;
NSData *_Nullable data = [[self loadedThumbnailSmallSync] dataAndReturnError:&error];
if (error || !data) {
OWSFail(@"Couldn't load thumbnail data: %@", error);
return nil;
}
return data;
}
- (NSArray<NSString *> *)allThumbnailPaths
{
NSMutableArray<NSString *> *result = [NSMutableArray new];
for (TSAttachmentThumbnail *thumbnail in self.thumbnails) {
NSString *_Nullable thumbnailPath = [self pathForThumbnail:thumbnail];
if (!thumbnailPath) {
OWSFail(@"Missing thumbnail path.");
continue;
}
[result addObject:thumbnailPath];
}
NSString *_Nullable legacyThumbnailPath = self.legacyThumbnailPath;
if (legacyThumbnailPath && [[NSFileManager defaultManager] fileExistsAtPath:legacyThumbnailPath]) {
[result addObject:legacyThumbnailPath];
}
return result;
}
#pragma mark - Update With... Methods
- (void)markForLazyRestoreWithFragment:(OWSBackupFragment *)lazyRestoreFragment
@ -858,7 +917,7 @@ const NSUInteger ThumbnailDimensionPointsLarge() {
- (nullable TSAttachmentStream *)cloneAsThumbnail
{
NSData *thumbnailData = self.legacyThumbnailData;
NSData *_Nullable thumbnailData = self.thumbnailDataSmallSync;
// Only some media types have thumbnails
if (!thumbnailData) {
return nil;

Loading…
Cancel
Save