ソースを参照

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