瀏覽代碼

OneDrive: implement file downloading

Note that we don't use the Live SDK for this, but the existing HTTP downloading class for simplicity and performance reasons
Felix Paul Kühne 10 年之前
父節點
當前提交
a53e2d1dd9

+ 2 - 0
Sources/VLCCloudStorageTableViewCell.m

@@ -146,11 +146,13 @@
         }
     } else if(_oneDriveFile != nil) {
         if (_oneDriveFile.isFolder) {
+            self.downloadButton.hidden = YES;
             self.folderTitleLabel.text = self.oneDriveFile.name;
             self.titleLabel.hidden = self.subtitleLabel.hidden = YES;
             self.folderTitleLabel.hidden = NO;
             self.thumbnailView.image = [UIImage imageNamed:@"folder"];
         } else {
+            self.downloadButton.hidden = NO;
             self.titleLabel.text = self.oneDriveFile.name;
             NSMutableString *subtitle = [[NSMutableString alloc] init];
             if (self.oneDriveFile.size > 0) {

+ 3 - 3
Sources/VLCOneDriveController.h

@@ -31,7 +31,7 @@
 
 @interface VLCOneDriveController : NSObject
 
-@property (nonatomic, weak) VLCOneDriveTableViewController *delegate;
+@property (nonatomic, weak) UIViewController <VLCOneDriveControllerDelegate>*delegate;
 @property (readonly) BOOL activeSession;
 @property (readonly) BOOL userAuthenticated;
 @property (nonatomic, readonly) VLCOneDriveObject *rootFolder;
@@ -42,8 +42,8 @@
 - (void)login;
 - (void)logout;
 
+- (void)downloadObject:(VLCOneDriveObject *)object;
+
 - (void)loadCurrentFolder;
-- (void)downloadFileWithPath:(NSString *)path;
-- (void)streamFileWithPath:(NSString *)path;
 
 @end

+ 66 - 16
Sources/VLCOneDriveController.m

@@ -21,11 +21,19 @@
 /* include private API headers */
 #import <LiveSDK/LiveApiHelper.h>
 
-@interface VLCOneDriveController () <LiveAuthDelegate, LiveDownloadOperationDelegate, VLCOneDriveObjectDelegate>
+@interface VLCOneDriveController () <LiveAuthDelegate, VLCOneDriveObjectDelegate, VLCOneDriveObjectDownloadDelegate>
 {
     LiveConnectClient *_liveClient;
     NSArray *_liveScopes;
     BOOL _activeSession;
+
+    NSMutableArray *_pendingDownloads;
+    BOOL _downloadInProgress;
+
+    CGFloat _averageSpeed;
+    CGFloat _fileSize;
+    NSTimeInterval _startDL;
+    NSTimeInterval _lastStatsUpdate;
 }
 
 @end
@@ -85,7 +93,7 @@
 
 - (void)authCompleted:(LiveConnectSessionStatus)status session:(LiveConnectSession *)session userState:(id)userState
 {
-    NSLog(@"authCompleted, status %i, state %@", status, userState);
+    APLog(@"OneDrive: authCompleted, status %i, state %@", status, userState);
 
     if (status == 1 && session != NULL && [userState isEqualToString:@"init"])
         _activeSession = YES;
@@ -151,40 +159,82 @@
 
 #pragma mark - file handling
 
-- (void)downloadFileWithPath:(NSString *)path
+- (void)downloadObject:(VLCOneDriveObject *)object
 {
+    if (object.isFolder)
+        return;
+
+    object.downloadDelegate = self;
+    if (!_pendingDownloads)
+        _pendingDownloads = [[NSMutableArray alloc] init];
+    [_pendingDownloads addObject:object];
+
+    [self _triggerNextDownload];
 }
 
-- (void)liveDownloadOperationProgressed:(LiveOperationProgress *)progress
-                                   data:(NSData *)receivedData
-                              operation:(LiveDownloadOperation *)operation
+- (void)_triggerNextDownload
 {
+    if (_pendingDownloads.count > 0 && !_downloadInProgress) {
+        _downloadInProgress = YES;
+        [_pendingDownloads[0] saveObjectToDocuments];
+        [_pendingDownloads removeObjectAtIndex:0];
+
+        if ([self.delegate respondsToSelector:@selector(numberOfFilesWaitingToBeDownloadedChanged)])
+            [self.delegate numberOfFilesWaitingToBeDownloadedChanged];
+    }
 }
 
-- (void)streamFileWithPath:(NSString *)path
+- (void)downloadStarted:(VLCOneDriveObject *)object
 {
+    _startDL = [NSDate timeIntervalSinceReferenceDate];
+    if ([self.delegate respondsToSelector:@selector(operationWithProgressInformationStarted)])
+        [self.delegate operationWithProgressInformationStarted];
 }
 
-#pragma mark - skydrive object delegation
+- (void)downloadEnded:(VLCOneDriveObject *)object
+{
+    if ([self.delegate respondsToSelector:@selector(operationWithProgressInformationStopped)])
+        [self.delegate operationWithProgressInformationStopped];
 
-- (void)folderContentLoaded:(VLCOneDriveObject *)sender
+    _downloadInProgress = NO;
+    [self _triggerNextDownload];
+}
+
+- (void)progressUpdated:(CGFloat)progress
 {
-    if (self.delegate)
-        [self.delegate performSelector:@selector(mediaListUpdated)];
+    if ([self.delegate respondsToSelector:@selector(currentProgressInformation:)])
+        [self.delegate currentProgressInformation:progress];
 }
 
-- (void)folderContentLoadingFailed:(NSError *)error sender:(VLCOneDriveObject *)sender
+- (void)calculateRemainingTime:(CGFloat)receivedDataSize expectedDownloadSize:(CGFloat)expectedDownloadSize
 {
-    APLog(@"folder content loading failed %@", error);
+    CGFloat lastSpeed = receivedDataSize / ([NSDate timeIntervalSinceReferenceDate] - _startDL);
+    CGFloat smoothingFactor = 0.005;
+    _averageSpeed = isnan(_averageSpeed) ? lastSpeed : smoothingFactor * lastSpeed + (1 - smoothingFactor) * _averageSpeed;
+
+    CGFloat RemainingInSeconds = (expectedDownloadSize - receivedDataSize)/_averageSpeed;
+
+    NSDate *date = [NSDate dateWithTimeIntervalSince1970:RemainingInSeconds];
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+    [formatter setDateFormat:@"HH:mm:ss"];
+    [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+
+    NSString  *remaingTime = [formatter stringFromDate:date];
+    if ([self.delegate respondsToSelector:@selector(updateRemainingTime:)])
+        [self.delegate updateRemainingTime:remaingTime];
 }
 
-- (void)fileContentLoaded:(VLCOneDriveObject *)sender
+#pragma mark - onedrive object delegation
+
+- (void)folderContentLoaded:(VLCOneDriveObject *)sender
 {
+    if (self.delegate)
+        [self.delegate performSelector:@selector(mediaListUpdated)];
 }
 
-- (void)fileContentLoadingFailed:(NSError *)error sender:(VLCOneDriveObject *)sender
+- (void)folderContentLoadingFailed:(NSError *)error sender:(VLCOneDriveObject *)sender
 {
-    APLog(@"file content loading failed %@", error);
+    APLog(@"folder content loading failed %@", error);
 }
 
 - (void)fullFolderTreeLoaded:(VLCOneDriveObject *)sender

+ 8 - 5
Sources/VLCOneDriveObject.h

@@ -22,12 +22,14 @@
 
 - (void)folderContentLoadingFailed:(NSError *)error
                             sender:(VLCOneDriveObject *) sender;
+@end
 
-- (void)fileContentLoaded:(VLCOneDriveObject *)sender;
-
-- (void)fileContentLoadingFailed:(NSError *)error
-                          sender:(VLCOneDriveObject *) sender;
+@protocol VLCOneDriveObjectDownloadDelegate <NSObject>
 
+- (void)downloadStarted:(VLCOneDriveObject *)object;
+- (void)downloadEnded:(VLCOneDriveObject *)object;
+- (void)progressUpdated:(CGFloat)progress;
+- (void)calculateRemainingTime:(CGFloat)receivedDataSize expectedDownloadSize:(CGFloat)expectedDownloadSize;
 @end
 
 @interface VLCOneDriveObject : NSObject <LiveOperationDelegate, LiveDownloadOperationDelegate, VLCOneDriveObjectDelegate>
@@ -53,8 +55,9 @@
 
 @property (strong, nonatomic) LiveConnectClient *liveClient;
 @property (strong, nonatomic) id<VLCOneDriveObjectDelegate>delegate;
+@property (strong, nonatomic) id<VLCOneDriveObjectDownloadDelegate>downloadDelegate;
 
 - (void)loadFolderContent;
-- (void)loadFileContent;
+- (void)saveObjectToDocuments;
 
 @end

+ 38 - 21
Sources/VLCOneDriveObject.m

@@ -11,6 +11,14 @@
  *****************************************************************************/
 
 #import "VLCOneDriveObject.h"
+#import "VLCHTTPFileDownloader.h"
+
+@interface VLCOneDriveObject () <VLCHTTPFileDownloader>
+{
+    VLCHTTPFileDownloader *_fileDownloader;
+}
+
+@end
 
 @implementation VLCOneDriveObject
 
@@ -60,7 +68,6 @@
 
 - (void)loadFolderContent
 {
-    NSLog(@"loadFolderContent");
     if (!self.isFolder) {
         APLog(@"%@ is no folder, can't load content", self.objectId);
         return;
@@ -85,25 +92,13 @@
     }
 }
 
-- (void)loadFileContent
-{
-}
-
 #pragma mark - live operations
 
 - (void)liveOperationSucceeded:(LiveDownloadOperation *)operation
 {
     NSString *userState = operation.userState;
 
-    NSLog(@"liveOperationSucceeded: %@", userState);
-
-    if ([userState isEqualToString:@"load-file-content"]) {
-//        LiveDownloadOperation *downloadOperation = (LiveDownloadOperation *)operation;
-
-        //FIXME: handle the incoming data!
-
-        [self.delegate fileContentLoaded:self];
-    } else if ([userState isEqualToString:@"load-folder-content"]) {
+    if ([userState isEqualToString:@"load-folder-content"]) {
         NSMutableArray *subFolders = [[NSMutableArray alloc] init];
         NSMutableArray *folderFiles = [[NSMutableArray alloc] init];
         NSMutableArray *items = [[NSMutableArray alloc] init];
@@ -146,14 +141,10 @@
 {
     NSString *userState = operation.userState;
 
-    NSLog(@"liveOperationFailed %@ (%@)", userState, error);
+    APLog(@"liveOperationFailed %@ (%@)", userState, error);
 
     if ([userState isEqualToString:@"load-folder-content"])
         [self.delegate folderContentLoadingFailed:error sender:self];
-    else if ([userState isEqualToString:@"load-file-content"])
-        [self.delegate fileContentLoadingFailed:error sender:self];
-    else
-        APLog(@"failing live operation with state %@ failed with error %@", userState, error);
 }
 
 #pragma mark - delegation
@@ -173,12 +164,38 @@
     [self loadFolderContent];
 }
 
-- (void)fileContentLoaded:(VLCOneDriveObject *)sender
+#pragma mark - file downloading
+
+- (void)saveObjectToDocuments
+{
+    _fileDownloader = [[VLCHTTPFileDownloader alloc] init];
+    _fileDownloader.delegate = self;
+    [_fileDownloader downloadFileFromURLwithFileName:[NSURL URLWithString:self.downloadPath] fileNameOfMedia:self.name];
+}
+
+- (void)downloadStarted
+{
+    if ([self.downloadDelegate respondsToSelector:@selector(downloadStarted:)])
+        [self.downloadDelegate downloadStarted:self];
+}
+
+- (void)downloadEnded
+{
+    if ([self.downloadDelegate respondsToSelector:@selector(downloadEnded:)])
+        [self.downloadDelegate downloadEnded:self];
+}
+
+- (void)downloadFailedWithErrorDescription:(NSString *)description
 {
+    APLog(@"download failed (%@)", description);
 }
 
-- (void)fileContentLoadingFailed:(NSError *)error sender:(VLCOneDriveObject *)sender
+- (void)progressUpdatedTo:(CGFloat)percentage receivedDataSize:(CGFloat)receivedDataSize expectedDownloadSize:(CGFloat)expectedDownloadSize
 {
+    if ([self.downloadDelegate respondsToSelector:@selector(progressUpdated:)])
+        [self.downloadDelegate progressUpdated:percentage];
+    if ([self.downloadDelegate respondsToSelector:@selector(calculateRemainingTime:expectedDownloadSize:)])
+        [self.downloadDelegate calculateRemainingTime:receivedDataSize expectedDownloadSize:expectedDownloadSize];
 }
 
 @end

+ 80 - 0
Sources/VLCOneDriveTableViewController.m

@@ -16,16 +16,22 @@
 #import "VLCCloudStorageTableViewCell.h"
 #import "VLCAppDelegate.h"
 #import "VLCOneDriveController.h"
+#import "VLCProgressView.h"
 
 @interface VLCOneDriveTableViewController () <UITableViewDataSource, UITableViewDelegate, VLCOneDriveControllerDelegate, VLCCloudStorageTableViewCell>
 {
     UIBarButtonItem *_backButton;
     UIBarButtonItem *_logoutButton;
+    UIBarButtonItem *_numberOfFilesBarButtonItem;
+    UIBarButtonItem *_progressBarButtonItem;
+    VLCProgressView *_progressView;
 
     UIActivityIndicatorView *_activityIndicator;
 
     VLCOneDriveController *_oneDriveController;
     NSString *_currentPath;
+
+    VLCOneDriveObject *_selectedFile;
 }
 @end
 
@@ -50,6 +56,12 @@
     self.tableView.separatorColor = [UIColor VLCDarkBackgroundColor];
     self.view.backgroundColor = [UIColor VLCDarkBackgroundColor];
 
+    _numberOfFilesBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"NUM_OF_FILES", nil), 0] style:UIBarButtonItemStylePlain target:nil action:nil];
+    [_numberOfFilesBarButtonItem setTitleTextAttributes:@{ UITextAttributeFont : [UIFont systemFontOfSize:11.] } forState:UIControlStateNormal];
+
+    _progressView = [VLCProgressView new];
+    _progressBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:_progressView];
+
     self.cloudStorageLogo = nil;
     if (!SYSTEM_RUNS_IOS7_OR_LATER) {
         self.flatLoginButton.hidden = YES;
@@ -59,6 +71,10 @@
         [self.flatLoginButton setTitle:NSLocalizedString(@"DROPBOX_LOGIN", nil) forState:UIControlStateNormal];
     }
 
+    [self.navigationController.toolbar setBackgroundImage:[UIImage imageNamed:@"sudHeaderBg"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
+
+    [self _showProgressInToolbar:NO];
+
     _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
     _activityIndicator.hidesWhenStopped = YES;
     _activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
@@ -70,6 +86,10 @@
 
 - (void)viewWillAppear:(BOOL)animated
 {
+    self.navigationController.toolbarHidden = NO;
+    self.navigationController.toolbar.barStyle = UIBarStyleBlack;
+    [self.navigationController.toolbar setBackgroundImage:[UIImage imageNamed:@"bottomBlackBar"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
+
     [super viewWillAppear:animated];
 
     if (_oneDriveController.activeSession)
@@ -82,6 +102,12 @@
     self.cloudStorageLogo.center = self.view.center;
 }
 
+- (void)viewWillDisappear:(BOOL)animated
+{
+    self.navigationController.toolbarHidden = YES;
+    [super viewWillDisappear:animated];
+}
+
 #pragma mark - generic interface interaction
 
 - (IBAction)goBack:(id)sender
@@ -148,6 +174,14 @@
     [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
 }
 
+- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
+{
+    if (buttonIndex == 1)
+        [_oneDriveController downloadObject:_selectedFile];
+
+    _selectedFile = nil;
+}
+
 #pragma mark - login dialog
 
 - (void)logout
@@ -175,6 +209,14 @@
     [_activityIndicator stopAnimating];
 
     [self.tableView reloadData];
+
+    NSUInteger count = _oneDriveController.currentFolder.items.count;
+    if (count == 0)
+        _numberOfFilesBarButtonItem.title = NSLocalizedString(@"NO_FILES", nil);
+    else if (count != 1)
+        _numberOfFilesBarButtonItem.title = [NSString stringWithFormat:NSLocalizedString(@"NUM_OF_FILES", nil), count];
+    else
+        _numberOfFilesBarButtonItem.title = NSLocalizedString(@"ONE_FILE", nil);
 }
 
 - (void)sessionWasUpdated
@@ -182,6 +224,39 @@
     [self updateViewAfterSessionChange];
 }
 
+#pragma mark - download visualization
+
+- (void)_showProgressInToolbar:(BOOL)value
+{
+    if (!value)
+        [self setToolbarItems:@[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], _numberOfFilesBarButtonItem, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]] animated:YES];
+    else {
+        _progressView.progressBar.progress = 0.;
+        [self setToolbarItems:@[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], _progressBarButtonItem, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]] animated:YES];
+    }
+}
+
+
+- (void)operationWithProgressInformationStarted
+{
+    [self _showProgressInToolbar:YES];
+}
+
+- (void)currentProgressInformation:(float)progress
+{
+    [_progressView.progressBar setProgress:progress animated:YES];
+}
+
+- (void)updateRemainingTime:(NSString *)time
+{
+    [_progressView updateTime:time];
+}
+
+- (void)operationWithProgressInformationStopped
+{
+    [self _showProgressInToolbar:NO];
+}
+
 #pragma mark - app delegate
 
 - (void)updateViewAfterSessionChange
@@ -206,6 +281,11 @@
 
 - (void)triggerDownloadForCell:(VLCCloudStorageTableViewCell *)cell
 {
+    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
+    _selectedFile = _oneDriveController.currentFolder.items[indexPath.row];
+
+    UIAlertView * alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"DROPBOX_DOWNLOAD", nil) message:[NSString stringWithFormat:NSLocalizedString(@"DROPBOX_DL_LONG", nil), _selectedFile.name, [[UIDevice currentDevice] model]] delegate:self cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) otherButtonTitles:NSLocalizedString(@"BUTTON_DOWNLOAD", nil), nil];
+    [alert show];
 }
 
 @end