mirror of
https://github.com/viq/NewsBlur.git
synced 2025-08-31 22:20:12 +00:00
Caching images for offline. Progress bar works, but the message is wrong.
This commit is contained in:
parent
cb3d205dd6
commit
787fe4e9ff
5 changed files with 99 additions and 41 deletions
|
@ -133,7 +133,7 @@
|
||||||
if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
|
if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
|
||||||
[NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
|
[NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self setRequestsCount:[self requestsCount]+1];
|
[self setRequestsCount:[self requestsCount]+1];
|
||||||
|
|
||||||
ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
|
ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "BaseViewController.h"
|
#import "BaseViewController.h"
|
||||||
#import "FMDatabaseQueue.h"
|
#import "FMDatabaseQueue.h"
|
||||||
|
#import "ASINetworkQueue.h"
|
||||||
|
|
||||||
#define FEED_DETAIL_VIEW_TAG 1000001
|
#define FEED_DETAIL_VIEW_TAG 1000001
|
||||||
#define STORY_DETAIL_VIEW_TAG 1000002
|
#define STORY_DETAIL_VIEW_TAG 1000002
|
||||||
|
@ -139,6 +140,7 @@
|
||||||
NSArray *categories;
|
NSArray *categories;
|
||||||
NSDictionary *categoryFeeds;
|
NSDictionary *categoryFeeds;
|
||||||
UIImageView *splashView;
|
UIImageView *splashView;
|
||||||
|
ASINetworkQueue *operationQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (nonatomic) IBOutlet UIWindow *window;
|
@property (nonatomic) IBOutlet UIWindow *window;
|
||||||
|
@ -233,6 +235,7 @@
|
||||||
@property (nonatomic) NSArray *categories;
|
@property (nonatomic) NSArray *categories;
|
||||||
@property (nonatomic) NSDictionary *categoryFeeds;
|
@property (nonatomic) NSDictionary *categoryFeeds;
|
||||||
@property (readwrite) FMDatabaseQueue *database;
|
@property (readwrite) FMDatabaseQueue *database;
|
||||||
|
@property (readwrite) ASINetworkQueue *operationQueue;
|
||||||
|
|
||||||
+ (NewsBlurAppDelegate*) sharedAppDelegate;
|
+ (NewsBlurAppDelegate*) sharedAppDelegate;
|
||||||
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;
|
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;
|
||||||
|
@ -338,6 +341,7 @@
|
||||||
- (void)storeAllUnreadStories:(ASIHTTPRequest *)request;
|
- (void)storeAllUnreadStories:(ASIHTTPRequest *)request;
|
||||||
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)())callback;
|
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)())callback;
|
||||||
- (void)syncQueuedReadStories:(FMDatabase *)db withStories:(NSDictionary *)hashes withCallback:(void(^)())callback;
|
- (void)syncQueuedReadStories:(FMDatabase *)db withStories:(NSDictionary *)hashes withCallback:(void(^)())callback;
|
||||||
|
- (void)cachedImageQueueFinished:(ASINetworkQueue *)queue;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#import "UserProfileViewController.h"
|
#import "UserProfileViewController.h"
|
||||||
#import "NBContainerViewController.h"
|
#import "NBContainerViewController.h"
|
||||||
#import "AFJSONRequestOperation.h"
|
#import "AFJSONRequestOperation.h"
|
||||||
|
#import "ASINetworkQueue.h"
|
||||||
#import "InteractionsModule.h"
|
#import "InteractionsModule.h"
|
||||||
#import "ActivityModule.h"
|
#import "ActivityModule.h"
|
||||||
#import "FirstTimeUserViewController.h"
|
#import "FirstTimeUserViewController.h"
|
||||||
|
@ -46,7 +47,7 @@
|
||||||
|
|
||||||
@implementation NewsBlurAppDelegate
|
@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 )
|
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
|
||||||
|
|
||||||
@synthesize window;
|
@synthesize window;
|
||||||
|
@ -144,6 +145,7 @@
|
||||||
@synthesize database;
|
@synthesize database;
|
||||||
@synthesize categories;
|
@synthesize categories;
|
||||||
@synthesize categoryFeeds;
|
@synthesize categoryFeeds;
|
||||||
|
@synthesize operationQueue;
|
||||||
|
|
||||||
+ (NewsBlurAppDelegate*) sharedAppDelegate {
|
+ (NewsBlurAppDelegate*) sharedAppDelegate {
|
||||||
return (NewsBlurAppDelegate*) [UIApplication sharedApplication].delegate;
|
return (NewsBlurAppDelegate*) [UIApplication sharedApplication].delegate;
|
||||||
|
@ -2108,6 +2110,7 @@
|
||||||
[db executeUpdate:@"drop table if exists `unread_hashes`"];
|
[db executeUpdate:@"drop table if exists `unread_hashes`"];
|
||||||
[db executeUpdate:@"drop table if exists `accounts`"];
|
[db executeUpdate:@"drop table if exists `accounts`"];
|
||||||
[db executeUpdate:@"drop table if exists `feeds`"];
|
[db executeUpdate:@"drop table if exists `feeds`"];
|
||||||
|
[db executeUpdate:@"drop table if exists `cached_images`"];
|
||||||
// [db executeUpdate:@"drop table if exists `queued_read_hashes`"];
|
// [db executeUpdate:@"drop table if exists `queued_read_hashes`"];
|
||||||
NSLog(@"Dropped db: %@", [db lastErrorMessage]);
|
NSLog(@"Dropped db: %@", [db lastErrorMessage]);
|
||||||
sqlite3_exec(db.sqliteHandle, [[NSString stringWithFormat:@"PRAGMA user_version = %d", CURRENT_DB_VERSION] UTF8String], NULL, NULL, NULL);
|
sqlite3_exec(db.sqliteHandle, [[NSString stringWithFormat:@"PRAGMA user_version = %d", CURRENT_DB_VERSION] UTF8String], NULL, NULL, NULL);
|
||||||
|
@ -2160,6 +2163,16 @@
|
||||||
")"];
|
")"];
|
||||||
[db executeUpdate:createReadTable];
|
[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]);
|
NSLog(@"Create db %d: %@", [db lastErrorCode], [db lastErrorMessage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2373,7 +2386,7 @@
|
||||||
(unsigned long)NULL), ^(void) {
|
(unsigned long)NULL), ^(void) {
|
||||||
[_self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
[_self.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
||||||
for (NSDictionary *story in [results objectForKey:@"stories"]) {
|
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_feed_id, story_hash, story_timestamp, image_url, story_json) VALUES "
|
||||||
"(?, ?, ?, ?, ?)",
|
"(?, ?, ?, ?, ?)",
|
||||||
[story objectForKey:@"story_feed_id"],
|
[story objectForKey:@"story_feed_id"],
|
||||||
|
@ -2382,6 +2395,15 @@
|
||||||
[story objectForKey:@"image_url"],
|
[story objectForKey:@"image_url"],
|
||||||
[story JSONRepresentation]
|
[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 && inserted) anySuccess = YES;
|
||||||
}
|
}
|
||||||
if (anySuccess) {
|
if (anySuccess) {
|
||||||
|
@ -2406,9 +2428,9 @@
|
||||||
NSMutableArray *urls = [NSMutableArray array];
|
NSMutableArray *urls = [NSMutableArray array];
|
||||||
|
|
||||||
[self.database inDatabase:^(FMDatabase *db) {
|
[self.database inDatabase:^(FMDatabase *db) {
|
||||||
NSString *commonQuery = @"FROM stories s "
|
NSString *commonQuery = @"FROM cached_images c "
|
||||||
"INNER JOIN unread_hashes u ON (s.story_hash = u.story_hash) "
|
"INNER JOIN unread_hashes u ON (c.story_hash = u.story_hash) "
|
||||||
"WHERE s.image_cached is null and s.image_url is not null";
|
"WHERE c.image_cached is null ";
|
||||||
int count = [db intForQuery:[NSString stringWithFormat:@"SELECT COUNT(1) %@", commonQuery]];
|
int count = [db intForQuery:[NSString stringWithFormat:@"SELECT COUNT(1) %@", commonQuery]];
|
||||||
if (self.totalUncachedImagesCount == 0) {
|
if (self.totalUncachedImagesCount == 0) {
|
||||||
self.totalUncachedImagesCount = count;
|
self.totalUncachedImagesCount = count;
|
||||||
|
@ -2424,10 +2446,12 @@
|
||||||
} else {
|
} else {
|
||||||
order = @"DESC";
|
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]) {
|
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 start = (int)[[NSDate date] timeIntervalSince1970];
|
||||||
int end = self.latestFetchedStoryDate;
|
int end = self.latestFetchedStoryDate;
|
||||||
|
@ -2449,6 +2473,9 @@
|
||||||
|
|
||||||
- (void)fetchAllUncachedImages {
|
- (void)fetchAllUncachedImages {
|
||||||
NSArray *urls = [self uncachedImageUrls];
|
NSArray *urls = [self uncachedImageUrls];
|
||||||
|
operationQueue = [[ASINetworkQueue alloc] init];
|
||||||
|
operationQueue.maxConcurrentOperationCount = 4;
|
||||||
|
operationQueue.delegate = self;
|
||||||
|
|
||||||
if ([urls count] == 0) {
|
if ([urls count] == 0) {
|
||||||
NSLog(@"Finished caching images. %d total", self.totalUncachedImagesCount);
|
NSLog(@"Finished caching images. %d total", self.totalUncachedImagesCount);
|
||||||
|
@ -2458,47 +2485,59 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (NSString *urlString in urls) {
|
for (NSArray *urlArray in urls) {
|
||||||
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
|
NSURL *url = [NSURL URLWithString:[urlArray objectAtIndex:0]];
|
||||||
// (unsigned long)NULL), ^(void) {
|
NSString *storyHash = [urlArray objectAtIndex:1];
|
||||||
// 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];
|
|
||||||
// });
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
|
||||||
[self.feedsViewController hideNotifier];
|
[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 {
|
- (void)storeCachedImage:(ASIHTTPRequest *)request {
|
||||||
NSData *responseData = [request responseData];
|
NSString *storyHash = [[request userInfo] objectForKey:@"story_hash"];
|
||||||
|
|
||||||
BOOL anySuccess = YES;
|
if ([request responseStatusCode] == 200) {
|
||||||
|
NSData *responseData = [request responseData];
|
||||||
if (anySuccess) {
|
NSString *md5Url = [Utilities md5:[[request originalURL] absoluteString]];
|
||||||
[self fetchAllUncachedImages];
|
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 {
|
} else {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
NSLog(@"Failed to fetch: %@ / %@", [[request originalURL] absoluteString], storyHash);
|
||||||
[self.feedsViewController hideNotifier];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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
|
@end
|
||||||
|
|
|
@ -21,5 +21,6 @@ void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor
|
||||||
+ (void)drawLinearGradientWithRect:(CGRect)rect startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor;
|
+ (void)drawLinearGradientWithRect:(CGRect)rect startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor;
|
||||||
+ (void)saveimagesToDisk;
|
+ (void)saveimagesToDisk;
|
||||||
+ (UIImage *)roundCorneredImage:(UIImage *)orig radius:(CGFloat)r;
|
+ (UIImage *)roundCorneredImage:(UIImage *)orig radius:(CGFloat)r;
|
||||||
|
+ (NSString *)md5:(NSString *)string;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "Utilities.h"
|
#import "Utilities.h"
|
||||||
|
#import <CommonCrypto/CommonCrypto.h>
|
||||||
|
|
||||||
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor,
|
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor,
|
||||||
CGColorRef endColor) {
|
CGColorRef endColor) {
|
||||||
|
@ -130,4 +131,17 @@ static NSMutableDictionary *imageCache;
|
||||||
return result;
|
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
|
@end
|
Loading…
Add table
Reference in a new issue