From 3b95f0ac255ef7f543eab467b42c69ea916ae688 Mon Sep 17 00:00:00 2001 From: Nick O'Neill Date: Mon, 23 Dec 2013 21:36:10 -0800 Subject: [PATCH] Added an uploader progress view. Based on notification stuff, so any other part of the app (or other people using the client) can get the same updates when the uploads start, move forward or end. Also fixed some issues with url parsing, should be not so sensitive about trailing slashes now. Change-Id: Ieafc3ecae03eeeba38beed55efad23703b11561c --- .../photobackup.xcodeproj/project.pbxproj | 10 ++-- .../Base.lproj/Main_iPhone.storyboard | 44 +++++++++++++++++- clients/ios-objc/photobackup/LAAppDelegate.m | 9 ++-- .../photobackup/LACamliClient/LACamliClient.h | 7 ++- .../photobackup/LACamliClient/LACamliClient.m | 35 +++++++++++--- .../LACamliClient/LACamliUploadOperation.m | 2 +- .../ios-objc/photobackup/LAViewController.h | 3 +- .../ios-objc/photobackup/LAViewController.m | 46 +++++++++++++++++-- .../photobackup/ProgressViewController.h | 16 +++++++ .../photobackup/ProgressViewController.m | 24 ++++++++++ 10 files changed, 171 insertions(+), 25 deletions(-) create mode 100644 clients/ios-objc/photobackup/ProgressViewController.h create mode 100644 clients/ios-objc/photobackup/ProgressViewController.m diff --git a/clients/ios-objc/photobackup.xcodeproj/project.pbxproj b/clients/ios-objc/photobackup.xcodeproj/project.pbxproj index 5dd2a6a19..8a3c383fd 100644 --- a/clients/ios-objc/photobackup.xcodeproj/project.pbxproj +++ b/clients/ios-objc/photobackup.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - D0180BEA185007C000E3487F /* credentials.plist in Resources */ = {isa = PBXBuildFile; fileRef = D0180BE9185007C000E3487F /* credentials.plist */; }; + D059B88F186944F2006BE899 /* ProgressViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D059B88E186944F2006BE899 /* ProgressViewController.m */; }; D075B282184944330054FED3 /* LACamliUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = D075B281184944330054FED3 /* LACamliUtil.m */; }; D075B28518494DB20054FED3 /* LACamliUploadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D075B28418494DB20054FED3 /* LACamliUploadOperation.m */; }; D095AE131814AF10008163F2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D095AE121814AF10008163F2 /* Foundation.framework */; }; @@ -43,7 +43,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - D0180BE9185007C000E3487F /* credentials.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = credentials.plist; sourceTree = ""; }; + D059B88D186944F2006BE899 /* ProgressViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProgressViewController.h; sourceTree = ""; }; + D059B88E186944F2006BE899 /* ProgressViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProgressViewController.m; sourceTree = ""; }; D075B280184944330054FED3 /* LACamliUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LACamliUtil.h; sourceTree = ""; }; D075B281184944330054FED3 /* LACamliUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LACamliUtil.m; sourceTree = ""; }; D075B28318494DB20054FED3 /* LACamliUploadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LACamliUploadOperation.h; sourceTree = ""; }; @@ -146,11 +147,12 @@ D0AC217018503A8E004BD4F3 /* CamliCollectionCell.m */, D0D45E9F185FE2BE00EBC0A2 /* SettingsViewController.h */, D0D45EA0185FE2BE00EBC0A2 /* SettingsViewController.m */, + D059B88D186944F2006BE899 /* ProgressViewController.h */, + D059B88E186944F2006BE899 /* ProgressViewController.m */, D095AE241814AF10008163F2 /* Main_iPhone.storyboard */, D095AE271814AF10008163F2 /* Main_iPad.storyboard */, D095AE2D1814AF10008163F2 /* Images.xcassets */, D095AE191814AF10008163F2 /* Supporting Files */, - D0180BE9185007C000E3487F /* credentials.plist */, ); path = photobackup; sourceTree = ""; @@ -286,7 +288,6 @@ buildActionMask = 2147483647; files = ( D095AE291814AF10008163F2 /* Main_iPad.storyboard in Resources */, - D0180BEA185007C000E3487F /* credentials.plist in Resources */, D095AE2E1814AF10008163F2 /* Images.xcassets in Resources */, D095AE261814AF10008163F2 /* Main_iPhone.storyboard in Resources */, D095AE1D1814AF10008163F2 /* InfoPlist.strings in Resources */, @@ -311,6 +312,7 @@ D095AE1F1814AF10008163F2 /* main.m in Sources */, D095AE511814B1B9008163F2 /* LACamliClient.m in Sources */, D0AC217118503A8E004BD4F3 /* CamliCollectionCell.m in Sources */, + D059B88F186944F2006BE899 /* ProgressViewController.m in Sources */, D095AE501814B1B9008163F2 /* LACamliFile.m in Sources */, D075B282184944330054FED3 /* LACamliUtil.m in Sources */, D095AE231814AF10008163F2 /* LAAppDelegate.m in Sources */, diff --git a/clients/ios-objc/photobackup/Base.lproj/Main_iPhone.storyboard b/clients/ios-objc/photobackup/Base.lproj/Main_iPhone.storyboard index 9be64c97b..230553eb8 100644 --- a/clients/ios-objc/photobackup/Base.lproj/Main_iPhone.storyboard +++ b/clients/ios-objc/photobackup/Base.lproj/Main_iPhone.storyboard @@ -1,5 +1,5 @@ - + @@ -102,7 +102,7 @@ - + @@ -170,6 +170,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clients/ios-objc/photobackup/LAAppDelegate.m b/clients/ios-objc/photobackup/LAAppDelegate.m index 456ec9698..3d1b4c302 100644 --- a/clients/ios-objc/photobackup/LAAppDelegate.m +++ b/clients/ios-objc/photobackup/LAAppDelegate.m @@ -23,7 +23,10 @@ [self loadCredentials]; self.library = [[ALAssetsLibrary alloc] init]; - // TODO: request access to the library first + + [[NSNotificationCenter defaultCenter] addObserverForName:CamliNotificationUploadProgress object:nil queue:nil usingBlock:^(NSNotification *note) { + [UIApplication sharedApplication].applicationIconBadgeNumber = [note.userInfo[@"remain"] intValue]; + }]; return YES; } @@ -68,9 +71,7 @@ if (![weakSelf.client fileAlreadyUploaded:file]) { filesToUpload++; - [weakSelf.client addFile:file withCompletion:^{ - [UIApplication sharedApplication].applicationIconBadgeNumber--; - }]; + [weakSelf.client addFile:file withCompletion:nil]; } else { LALog(@"file already uploaded: %@",file.blobRef); } diff --git a/clients/ios-objc/photobackup/LACamliClient/LACamliClient.h b/clients/ios-objc/photobackup/LACamliClient/LACamliClient.h index ba931ba04..5ff7fc9db 100644 --- a/clients/ios-objc/photobackup/LACamliClient/LACamliClient.h +++ b/clients/ios-objc/photobackup/LACamliClient/LACamliClient.h @@ -11,15 +11,20 @@ @interface LACamliClient : NSObject +extern NSString *const CamliNotificationUploadStart; +extern NSString *const CamliNotificationUploadProgress; +extern NSString *const CamliNotificationUploadEnd; +extern NSString *const CamliBlobRootComponent; + @property NSURLSession *session; @property NSURL *serverURL; @property NSString *username; @property NSString *password; -@property NSString *blobRoot; @property NSURL *uploadUrl; @property NSOperationQueue *uploadQueue; +@property NSUInteger totalUploads; @property NSMutableArray *uploadedBlobRefs; @property UIBackgroundTaskIdentifier backgroundID; diff --git a/clients/ios-objc/photobackup/LACamliClient/LACamliClient.m b/clients/ios-objc/photobackup/LACamliClient/LACamliClient.m index 04899a328..32dd5b0cb 100644 --- a/clients/ios-objc/photobackup/LACamliClient/LACamliClient.m +++ b/clients/ios-objc/photobackup/LACamliClient/LACamliClient.m @@ -12,6 +12,11 @@ @implementation LACamliClient +NSString *const CamliNotificationUploadStart = @"camli-upload-start"; +NSString *const CamliNotificationUploadProgress = @"camli-upload-progress"; +NSString *const CamliNotificationUploadEnd = @"camli-upload-end"; +NSString *const CamliBlobRootComponent = @"bs-recv"; + - (id)initWithServer:(NSURL *)server username:(NSString *)username andPassword:(NSString *)password { NSParameterAssert(server); @@ -32,6 +37,7 @@ self.uploadQueue = [[NSOperationQueue alloc] init]; self.uploadQueue.maxConcurrentOperationCount = 1; + self.totalUploads = 0; self.isAuthorized = false; self.authorizing = false; @@ -63,7 +69,6 @@ #pragma mark - discovery -// if we don't have blobroot with which to make these requests, we need to find it first - (void)discoveryWithUsername:(NSString *)user andPassword:(NSString *)pass { self.authorizing = YES; @@ -86,10 +91,6 @@ if (res.statusCode != 200) { LALog(@"error with discovery: %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); } else { - NSError *parseError; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError]; - - self.blobRoot = json[@"blobRoot"]; self.isAuthorized = YES; [self.uploadQueue setSuspended:NO]; @@ -118,6 +119,12 @@ - (void)addFile:(LACamliFile *)file withCompletion:(void (^)())completion { NSParameterAssert(file); + + if (self.totalUploads == 0) { + [[NSNotificationCenter defaultCenter] postNotificationName:CamliNotificationUploadStart object:nil]; + } + + self.totalUploads++; if (![self isAuthorized]) { [self.uploadQueue setSuspended:YES]; @@ -133,7 +140,16 @@ [self.uploadedBlobRefs addObject:file.blobRef]; [self.uploadedBlobRefs writeToFile:[self uploadedBlobRefArchivePath] atomically:YES]; - completion(); + // let others know about upload progress + [[NSNotificationCenter defaultCenter] postNotificationName:CamliNotificationUploadProgress object:nil userInfo:@{@"total": @(self.totalUploads), @"remain": @([self.uploadQueue operationCount])}]; + + if (![self.uploadQueue operationCount]) { + self.totalUploads = 0; + [[NSNotificationCenter defaultCenter] postNotificationName:CamliNotificationUploadEnd object:nil]; + } + if (completion) { + completion(); + } }; [self.uploadQueue addOperation:op]; @@ -141,7 +157,7 @@ - (NSURL *)statUrl { - return [[self.serverURL URLByAppendingPathComponent:self.blobRoot] URLByAppendingPathComponent:@"camli/stat"]; + return [[self blobRoot] URLByAppendingPathComponent:@"camli/stat"]; } #pragma mark - getting stuff @@ -173,6 +189,11 @@ #pragma mark - utility +- (NSURL *)blobRoot +{ + return [self.serverURL URLByAppendingPathComponent:CamliBlobRootComponent]; +} + - (NSString *)encodedAuth { NSString *auth = [NSString stringWithFormat:@"%@:%@",self.username,self.password]; diff --git a/clients/ios-objc/photobackup/LACamliClient/LACamliUploadOperation.m b/clients/ios-objc/photobackup/LACamliClient/LACamliUploadOperation.m index 8beb00e2e..f94c6658e 100644 --- a/clients/ios-objc/photobackup/LACamliClient/LACamliUploadOperation.m +++ b/clients/ios-objc/photobackup/LACamliClient/LACamliUploadOperation.m @@ -64,7 +64,7 @@ static NSString *const multipartBoundary = @"Qe43VdbVVaGtkkMd"; for (NSString *key in params) { formValues = [formValues stringByAppendingString:[NSString stringWithFormat:@"%@=%@&",key,params[key]]]; } - + NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[self.client statUrl]]; [req setHTTPMethod:@"POST"]; [req setHTTPBody:[formValues dataUsingEncoding:NSUTF8StringEncoding]]; diff --git a/clients/ios-objc/photobackup/LAViewController.h b/clients/ios-objc/photobackup/LAViewController.h index 45782e919..45ecfe616 100644 --- a/clients/ios-objc/photobackup/LAViewController.h +++ b/clients/ios-objc/photobackup/LAViewController.h @@ -8,11 +8,12 @@ #import -@class LACamliClient; +@class LACamliClient,ProgressViewController; @interface LAViewController : UIViewController @property LACamliClient *client; +@property ProgressViewController *progress; - (void)dismissSettings; diff --git a/clients/ios-objc/photobackup/LAViewController.m b/clients/ios-objc/photobackup/LAViewController.m index b602773a8..29114ed1c 100644 --- a/clients/ios-objc/photobackup/LAViewController.m +++ b/clients/ios-objc/photobackup/LAViewController.m @@ -11,10 +11,7 @@ #import "LAAppDelegate.h" #import "LACamliUtil.h" #import "SettingsViewController.h" - -@interface LAViewController () - -@end +#import "ProgressViewController.h" @implementation LAViewController @@ -26,7 +23,6 @@ [self.navigationItem setRightBarButtonItem:settingsItem]; - // show the NSURL *serverURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] stringForKey:CamliServerKey]]; NSString *username = [[NSUserDefaults standardUserDefaults] stringForKey:CamliUsernameKey]; @@ -39,6 +35,41 @@ [self showSettings]; } + self.progress = [[self storyboard] instantiateViewControllerWithIdentifier:@"uploadBar"]; + self.progress.view.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height+self.progress.view.frame.size.height, self.progress.view.frame.size.width, self.progress.view.frame.size.height); + + [self.view addSubview:self.progress.view]; + + [[NSNotificationCenter defaultCenter] addObserverForName:CamliNotificationUploadStart object:nil queue:nil usingBlock:^(NSNotification *note) { + + dispatch_async(dispatch_get_main_queue(), ^{ + [UIView animateWithDuration:1.0 animations:^{ + self.progress.view.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-self.progress.view.frame.size.height, self.progress.view.frame.size.width, self.progress.view.frame.size.height); + }]; + }); + }]; + + [[NSNotificationCenter defaultCenter] addObserverForName:CamliNotificationUploadProgress object:nil queue:nil usingBlock:^(NSNotification *note) { + LALog(@"got progress %@ %@",note.userInfo[@"total"],note.userInfo[@"remain"]); + + NSUInteger total = [note.userInfo[@"total"] intValue]; + NSUInteger remain = [note.userInfo[@"remain"] intValue]; + + dispatch_async(dispatch_get_main_queue(), ^{ + self.progress.uploadLabel.text = [NSString stringWithFormat:@"Uploading %d of %d",total-remain,total]; + self.progress.uploadProgress.progress = (float)(total-remain)/(float)total; + }); + }]; + + [[NSNotificationCenter defaultCenter] addObserverForName:CamliNotificationUploadEnd object:nil queue:nil usingBlock:^(NSNotification *note) { + + dispatch_async(dispatch_get_main_queue(), ^{ + [UIView animateWithDuration:1.0 animations:^{ + self.progress.view.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height+self.progress.view.frame.size.height, self.progress.view.frame.size.width, self.progress.view.frame.size.height); + }]; + }); + }]; + // [self.client getRecentItemsWithCompletion:^(NSArray *objects) { // LALog(@"got objects: %@",objects); // }]; @@ -86,4 +117,9 @@ // Dispose of any resources that can be recreated. } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + @end diff --git a/clients/ios-objc/photobackup/ProgressViewController.h b/clients/ios-objc/photobackup/ProgressViewController.h new file mode 100644 index 000000000..71bea4a78 --- /dev/null +++ b/clients/ios-objc/photobackup/ProgressViewController.h @@ -0,0 +1,16 @@ +// +// ProgressViewController.h +// photobackup +// +// Created by Nick O'Neill on 12/23/13. +// Copyright (c) 2013 The Camlistore Authors. All rights reserved. +// + +#import + +@interface ProgressViewController : UIViewController + +@property IBOutlet UILabel *uploadLabel; +@property IBOutlet UIProgressView *uploadProgress; + +@end diff --git a/clients/ios-objc/photobackup/ProgressViewController.m b/clients/ios-objc/photobackup/ProgressViewController.m new file mode 100644 index 000000000..bc7f59074 --- /dev/null +++ b/clients/ios-objc/photobackup/ProgressViewController.m @@ -0,0 +1,24 @@ +// +// ProgressViewController.m +// photobackup +// +// Created by Nick O'Neill on 12/23/13. +// Copyright (c) 2013 The Camlistore Authors. All rights reserved. +// + +#import "ProgressViewController.h" + +@implementation ProgressViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end