Caching images for offline. Progress bar works, but the message is wrong.

This commit is contained in:
Samuel Clay 2013-06-22 19:34:12 -07:00
parent cb3d205dd6
commit 787fe4e9ff
5 changed files with 99 additions and 41 deletions

View file

@ -133,7 +133,7 @@
if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
[NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
}
[self setRequestsCount:[self requestsCount]+1];
ASIHTTPRequest *request = (ASIHTTPRequest *)operation;

View file

@ -9,6 +9,7 @@
#import <UIKit/UIKit.h>
#import "BaseViewController.h"
#import "FMDatabaseQueue.h"
#import "ASINetworkQueue.h"
#define FEED_DETAIL_VIEW_TAG 1000001
#define STORY_DETAIL_VIEW_TAG 1000002
@ -139,6 +140,7 @@
NSArray *categories;
NSDictionary *categoryFeeds;
UIImageView *splashView;
ASINetworkQueue *operationQueue;
}
@property (nonatomic) IBOutlet UIWindow *window;
@ -233,6 +235,7 @@
@property (nonatomic) NSArray *categories;
@property (nonatomic) NSDictionary *categoryFeeds;
@property (readwrite) FMDatabaseQueue *database;
@property (readwrite) ASINetworkQueue *operationQueue;
+ (NewsBlurAppDelegate*) sharedAppDelegate;
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;
@ -338,6 +341,7 @@
- (void)storeAllUnreadStories:(ASIHTTPRequest *)request;
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)())callback;
- (void)syncQueuedReadStories:(FMDatabase *)db withStories:(NSDictionary *)hashes withCallback:(void(^)())callback;
- (void)cachedImageQueueFinished:(ASINetworkQueue *)queue;
@end

View file

@ -26,6 +26,7 @@
#import "UserProfileViewController.h"
#import "NBContainerViewController.h"
#import "AFJSONRequestOperation.h"
#import "ASINetworkQueue.h"
#import "InteractionsModule.h"
#import "ActivityModule.h"
#import "FirstTimeUserViewController.h"
@ -46,7 +47,7 @@
@implementation NewsBlurAppDelegate
#define CURRENT_DB_VERSION 13
#define CURRENT_DB_VERSION 16
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
@synthesize window;
@ -144,6 +145,7 @@
@synthesize database;
@synthesize categories;
@synthesize categoryFeeds;
@synthesize operationQueue;
+ (NewsBlurAppDelegate*) sharedAppDelegate {
return (NewsBlurAppDelegate*) [UIApplication sharedApplication].delegate;
@ -2108,6 +2110,7 @@
[db executeUpdate:@"drop table if exists `unread_hashes`"];
[db executeUpdate:@"drop table if exists `accounts`"];
[db executeUpdate:@"drop table if exists `feeds`"];
[db executeUpdate:@"drop table if exists `cached_images`"];
// [db executeUpdate:@"drop table if exists `queued_read_hashes`"];
NSLog(@"Dropped db: %@", [db lastErrorMessage]);
sqlite3_exec(db.sqliteHandle, [[NSString stringWithFormat:@"PRAGMA user_version = %d", CURRENT_DB_VERSION] UTF8String], NULL, NULL, NULL);
@ -2160,6 +2163,16 @@
")"];
[db executeUpdate:createReadTable];
NSString *createImagesTable = [NSString stringWithFormat:@"create table if not exists cached_images "
"("
" story_feed_id number,"
" story_hash varchar(24),"
" image_url varchar(1024),"
" image_cached boolean,"
" UNIQUE(story_hash) ON CONFLICT IGNORE"
")"];
[db executeUpdate:createImagesTable];
NSLog(@"Create db %d: %@", [db lastErrorCode], [db lastErrorMessage]);
}
@ -2373,7 +2386,7 @@
(unsigned long)NULL), ^(void) {
[_self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
for (NSDictionary *story in [results objectForKey:@"stories"]) {
BOOL inserted = [db executeUpdate:@"INSERT into stories"
BOOL inserted = [db executeUpdate:@"INSERT into stories "
"(story_feed_id, story_hash, story_timestamp, image_url, story_json) VALUES "
"(?, ?, ?, ?, ?)",
[story objectForKey:@"story_feed_id"],
@ -2382,6 +2395,15 @@
[story objectForKey:@"image_url"],
[story JSONRepresentation]
];
if ([[story objectForKey:@"image_url"] class] != [NSNull class]) {
[db executeUpdate:@"INSERT INTO cached_images "
"(story_feed_id, story_hash, image_url) VALUES "
"(?, ?, ?)",
[story objectForKey:@"story_feed_id"],
[story objectForKey:@"story_hash"],
[story objectForKey:@"image_url"]
];
}
if (!anySuccess && inserted) anySuccess = YES;
}
if (anySuccess) {
@ -2406,9 +2428,9 @@
NSMutableArray *urls = [NSMutableArray array];
[self.database inDatabase:^(FMDatabase *db) {
NSString *commonQuery = @"FROM stories s "
"INNER JOIN unread_hashes u ON (s.story_hash = u.story_hash) "
"WHERE s.image_cached is null and s.image_url is not null";
NSString *commonQuery = @"FROM cached_images c "
"INNER JOIN unread_hashes u ON (c.story_hash = u.story_hash) "
"WHERE c.image_cached is null ";
int count = [db intForQuery:[NSString stringWithFormat:@"SELECT COUNT(1) %@", commonQuery]];
if (self.totalUncachedImagesCount == 0) {
self.totalUncachedImagesCount = count;
@ -2424,10 +2446,12 @@
} else {
order = @"DESC";
}
FMResultSet *cursor = [db executeQuery:[NSString stringWithFormat:@"SELECT s.image_url %@ ORDER BY u.story_timestamp %@ LIMIT %d", commonQuery, order, limit]];
NSString *sql = [NSString stringWithFormat:@"SELECT c.image_url, c.story_hash %@ ORDER BY u.story_timestamp %@ LIMIT %d", commonQuery, order, limit];
FMResultSet *cursor = [db executeQuery:sql];
while ([cursor next]) {
[urls addObject:[cursor objectForColumnName:@"image_url"]];
[urls addObject:@[[cursor objectForColumnName:@"image_url"],
[cursor objectForColumnName:@"story_hash"]]];
}
int start = (int)[[NSDate date] timeIntervalSince1970];
int end = self.latestFetchedStoryDate;
@ -2449,6 +2473,9 @@
- (void)fetchAllUncachedImages {
NSArray *urls = [self uncachedImageUrls];
operationQueue = [[ASINetworkQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 4;
operationQueue.delegate = self;
if ([urls count] == 0) {
NSLog(@"Finished caching images. %d total", self.totalUncachedImagesCount);
@ -2458,47 +2485,59 @@
return;
}
for (NSString *urlString in urls) {
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
// (unsigned long)NULL), ^(void) {
// NSURL *url = [NSURL URLWithString:urlString];
// ASIHTTPRequest *_request = [ASIHTTPRequest requestWithURL:url];
// __weak ASIHTTPRequest *request = _request;
// [request setResponseEncoding:NSUTF8StringEncoding];
// [request setDefaultResponseEncoding:NSUTF8StringEncoding];
// [request setFailedBlock:^(void) {
// NSLog(@"Failed cache image url.");
// }];
// [request setCompletionBlock:^(void) {
// [self storeCachedImage:request];
// }];
// [request setTimeOutSeconds:30];
// [request startAsynchronous];
// });
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
// (unsigned long)NULL), ^(void) {
//
// [self fetchAllUncachedImages];
// });
for (NSArray *urlArray in urls) {
NSURL *url = [NSURL URLWithString:[urlArray objectAtIndex:0]];
NSString *storyHash = [urlArray objectAtIndex:1];
dispatch_async(dispatch_get_main_queue(), ^{
[self.feedsViewController hideNotifier];
});
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUserInfo:@{@"story_hash": storyHash}];
[request setDelegate:self];
[request setDidFinishSelector:@selector(storeCachedImage:)];
[request setDidFailSelector:@selector(storeCachedImage:)];
[request setTimeOutSeconds:5];
[operationQueue addOperation:request];
}
[operationQueue setQueueDidFinishSelector:@selector(cachedImageQueueFinished:)];
[operationQueue setShouldCancelAllRequestsOnFailure:NO];
[operationQueue go];
// dispatch_async(dispatch_get_main_queue(), ^{
// [self.feedsViewController hideNotifier];
// });
}
- (void)storeCachedImage:(ASIHTTPRequest *)request {
NSData *responseData = [request responseData];
NSString *storyHash = [[request userInfo] objectForKey:@"story_hash"];
BOOL anySuccess = YES;
if (anySuccess) {
[self fetchAllUncachedImages];
if ([request responseStatusCode] == 200) {
NSData *responseData = [request responseData];
NSString *md5Url = [Utilities md5:[[request originalURL] absoluteString]];
NSLog(@"Storing image: %@ (%d bytes - %d in queue)", storyHash, [responseData length], [operationQueue requestsCount]);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
NSString *fullPath = [cacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@", md5Url]];
[fileManager createFileAtPath:fullPath contents:responseData attributes:nil];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self.feedsViewController hideNotifier];
});
NSLog(@"Failed to fetch: %@ / %@", [[request originalURL] absoluteString], storyHash);
}
[self.database inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"UPDATE cached_images SET "
"image_cached = 1 WHERE story_hash = ?",
storyHash];
}];
}
- (void)cachedImageQueueFinished:(ASINetworkQueue *)queue {
NSLog(@"Queue finished: %d total (%d remaining)", self.totalUncachedImagesCount, self.remainingUncachedImagesCount);
[self fetchAllUncachedImages];
// dispatch_async(dispatch_get_main_queue(), ^{
// [self.feedsViewController hideNotifier];
// });
}
@end

View file

@ -21,5 +21,6 @@ void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor
+ (void)drawLinearGradientWithRect:(CGRect)rect startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor;
+ (void)saveimagesToDisk;
+ (UIImage *)roundCorneredImage:(UIImage *)orig radius:(CGFloat)r;
+ (NSString *)md5:(NSString *)string;
@end

View file

@ -7,6 +7,7 @@
//
#import "Utilities.h"
#import <CommonCrypto/CommonCrypto.h>
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor,
CGColorRef endColor) {
@ -130,4 +131,17 @@ static NSMutableDictionary *imageCache;
return result;
}
+ (NSString *)md5:(NSString *)string {
const char *cStr = [string UTF8String];
unsigned char result[16];
CC_MD5( cStr, strlen(cStr), result ); // This is the md5 call
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
@end