Improve open file UX
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Fri, 28 Dec 2012 19:58:21 +0000 (21:58 +0200)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Fri, 28 Dec 2012 19:59:45 +0000 (21:59 +0200)
Allow cancelation of download, or removal from cache, from file details. When opening a non downloaded file, load file details view and start downloading. Improve cache management. Fix bugs. Improve open file menu placement in iPad, in iOS 6.

Classes/AccountManager.h
Classes/AccountManager.m
Classes/FolderViewController.m
Classes/OpenStackAppDelegate.h
Classes/OpenStackAppDelegate.m
Classes/SettingsViewController.m
Classes/StorageObjectViewController.h
Classes/StorageObjectViewController.m
OpenStack-Info.plist

index 3475180..57009a3 100755 (executable)
 @interface AccountManager : NSObject {
     OpenStackAccount *account;
     ASINetworkQueue *queue;
-    NSMutableDictionary *objectDownloadRequests;
 }
 
 @property (nonatomic, retain) ASINetworkQueue *queue;
 
 @property (nonatomic, assign) OpenStackAccount *account;
-@property (nonatomic, retain) NSMutableDictionary *objectDownloadRequests;
 
 - (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier;
 - (void)notify:(NSString *)name request:(OpenStackRequest *)request;
index 4e89a6a..66f68f6 100755 (executable)
@@ -22,7 +22,7 @@
 
 @implementation AccountManager
 
-@synthesize account, queue, objectDownloadRequests;
+@synthesize account, queue;
 
 #pragma mark - Callbacks
 
     if (requestUserInfo) {
         request.userInfo = requestUserInfo;
     }
-    if (!objectDownloadRequests)
-        self.objectDownloadRequests = [NSMutableDictionary dictionary];
-    [objectDownloadRequests setObject:request forKey:object.fullPath];
-    return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
+    
+    OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+    [app setObjectDownloadRequest:request forAccount:account container:container object:object];
+    return [self callbackWithRequest:request
+    success:^(OpenStackRequest *request) {
         if (!object.hash)
             object.hash = [request.responseHeaders objectForKey:@"X-Object-Hash"];
-        OpenStackAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
-        NSString *filePath = [appDelegate.cacheDirectoryPath stringByAppendingFormat:@"/%@.%@", object.hash, object.name.pathExtension];
+        OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+        NSString *filePath = [app.cacheDirectoryPath stringByAppendingFormat:@"/%@.%@", object.hash, object.name.pathExtension];
         [[request responseData] writeToFile:filePath atomically:YES];
-        @synchronized(appDelegate.cachedObjectsDictionary) {
-            [appDelegate.cachedObjectsDictionary setObject:filePath forKey:object.hash];
-            [appDelegate saveCacheDictionary];
-        }
-        [objectDownloadRequests removeObjectForKey:object.fullPath];
+        [app setCacheFilePath:filePath forHash:object.hash];
+        [app removeObjectDownloadRequestForAccount:account container:container object:object];
     }
-    failure:^(OpenStackRequest *request) { 
-        [objectDownloadRequests removeObjectForKey:object.fullPath];
+    failure:^(OpenStackRequest *request) {
+        OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+        [app removeObjectDownloadRequestForAccount:account container:container object:object];
     }];
 }
 
     return [self callbackWithRequest:request];
 }
 
-#pragma mark -
-#pragma mark Memory Management
+#pragma mark - Memory Management
 
 - (void)dealloc {
-    [objectDownloadRequests release];
     [super dealloc];
 }
 
index f8a3f5f..d73d3a1 100755 (executable)
@@ -527,18 +527,9 @@ return [self.folder sortedContentsUsingFilter:self.searchFilter
                 [vc release];
             } else {
                 StorageObject *cellObject = (StorageObject *)item;
-                BOOL fileDownloaded = NO;
                 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
-                NSString *filePath = [app.cachedObjectsDictionary objectForKey:cellObject.hash];
+                NSString *filePath = [app cacheFilePathForHash:cellObject.hash];
                 if (filePath) {
-                    if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
-                        fileDownloaded = YES;
-                    } else {
-                        [app.cachedObjectsDictionary removeObjectForKey:cellObject.hash];
-                        [app saveCacheDictionary];
-                    }
-                }
-                if (fileDownloaded) {
                     self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
                     self.documentInteractionController.delegate = self;
                     self.documentInteractionController.name = cellObject.name;
@@ -560,7 +551,15 @@ return [self.folder sortedContentsUsingFilter:self.searchFilter
                         }
                     }
                 } else {
-                    // XXX else load download view controller
+                    StorageObjectViewController *vc = [[StorageObjectViewController alloc] initWithNibName:@"StorageObjectViewController" bundle:nil];
+                    vc.account = self.account;
+                    vc.container = self.container;
+                    vc.folder = self.folder;
+                    vc.object = cellObject;
+                    vc.folderViewController = self;
+                    [self.navigationController pushViewController:vc animated:YES];
+                    [vc downloadFileForAction:StorageObjectActionOpenFile];
+                    [vc release];
                 }
                 [aTableView deselectRowAtIndexPath:indexPath animated:YES];
             }
index cefbac3..e3a35c4 100755 (executable)
@@ -6,7 +6,7 @@
 //  The OpenStack project is provided under the Apache 2.0 license.
 //
 
-@class RootViewController;
+@class RootViewController, OpenStackRequest, OpenStackAccount, Container, StorageObject;
 
 #define CACHED_OBJECTS_ARCHIVE_KEY @"cachedObjectsDictionary"
 #define CACHE_DICT_ARCHIVE_NAME @"cache.dict"
@@ -22,6 +22,7 @@
     NSMutableDictionary *cachedObjectsDictionary;
     NSString *cacheDictionaryFilePath;
     NSString *cacheDirectoryPath;
+    NSMutableDictionary *objectDownloadRequests;
     
     BOOL gotToken;
 }
 @property (nonatomic, retain) NSMutableDictionary *cachedObjectsDictionary;
 @property (nonatomic, copy) NSString *cacheDictionaryFilePath;
 @property (nonatomic, copy) NSString *cacheDirectoryPath;
+@property (nonatomic, readonly) NSMutableDictionary *objectDownloadRequests;
 
+- (NSString *)cacheFilePathForHash:(NSString *)hash;
+- (void)setCacheFilePath:(NSString *)filePath forHash:(NSString *)hash;
+- (BOOL)removeCacheObjectForHash:(NSString *)hash;
+- (void)removeAllCacheObjects;
 - (void)saveCacheDictionary;
 
+- (OpenStackRequest *)objectDownloadRequestForAccount:(OpenStackAccount *)account
+                                            container:(Container *)container
+                                               object:(StorageObject *)object;
+- (void)setObjectDownloadRequest:(OpenStackRequest *)request
+                      forAccount:(OpenStackAccount *)account
+                       container:(Container *)container
+                          object:(StorageObject *)object;
+- (void)removeObjectDownloadRequestForAccount:(OpenStackAccount *)account
+                                    container:(Container *)container
+                                       object:(StorageObject *)object;
+
 @end
 
index 31b5d92..a073ff1 100755 (executable)
@@ -8,17 +8,15 @@
 
 #import "OpenStackAppDelegate.h"
 #import "RootViewController.h"
-#import "OpenStackAccount.h"
 #import "Keychain.h"
-
 #import "JSON.h"
 #import "Archiver.h"
 #import "Provider.h"
-
 #import "OpenStackAccount.h"
-
+#import "Container.h"
+#import "StorageObject.h"
+#import "OpenStackRequest.h"
 #import "PithosImageViewController.h"
-
 #import "RootViewController.h"
 #import "PasscodeViewController.h"
 #import "AccountDetailsViewController.h"
@@ -37,6 +35,7 @@
 @synthesize cachedObjectsDictionary;
 @synthesize cacheDictionaryFilePath;
 @synthesize cacheDirectoryPath;
+@synthesize objectDownloadRequests;
 
 - (void)loadSettingsDefaults {
     // if settings haven't been set up yet, let's go ahead and set some sensible defaults
@@ -59,6 +58,8 @@
         [userDefaults setValue:cachedObjectsDictionary forKey:@"cachedObjectsDictionary"];
     }
     
+    objectDownloadRequests = [[NSMutableDictionary alloc] init];
+    
     [userDefaults synchronize];
 }
 
                                                                       animated:NO];
 }
 
-#pragma mark -
-#pragma mark Application lifecycle
+#pragma mark - Application lifecycle
 
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
     [self loadSettingsDefaults];
 
 #pragma mark - Persistence
 
+- (NSString *)cacheFilePathForHash:(NSString *)hash {
+    NSString *filePath = [self.cachedObjectsDictionary objectForKey:hash];
+    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+        @synchronized(self.cachedObjectsDictionary) {
+            [self.cachedObjectsDictionary removeObjectForKey:hash];
+        }
+        [self saveCacheDictionary];
+        filePath = nil;
+    }
+    return filePath;
+}
+
+- (void)setCacheFilePath:(NSString *)filePath forHash:(NSString *)hash {
+    @synchronized(self.cachedObjectsDictionary) {
+        [self.cachedObjectsDictionary setObject:filePath forKey:hash];
+        [self saveCacheDictionary];
+    }
+}
+
+- (BOOL)removeCacheObjectForHash:(NSString *)hash {
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSError *error = nil;
+    NSString *filePath = [self.cachedObjectsDictionary objectForKey:hash];
+    BOOL removed = NO;
+    @synchronized(self.cachedObjectsDictionary) {
+        [fileManager removeItemAtPath:filePath error:&error];
+        if (error) {
+            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
+                                                            message:[NSString stringWithFormat:@"Error in removing cached file, %@", error.localizedDescription]
+                                                           delegate:nil
+                                                  cancelButtonTitle:@"OK"
+                                                  otherButtonTitles:nil];
+            [alert show];
+            [alert release];
+        } else {
+            removed = YES;
+            [self.cachedObjectsDictionary removeObjectForKey:hash];
+        }
+    }
+    [self saveCacheDictionary];
+    return removed;
+}
+
+- (void)removeAllCacheObjects {
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSError *error = nil;
+    NSString *filePath;
+    NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:self.cacheDirectoryPath];
+    @synchronized(self.cachedObjectsDictionary) {
+        while (filePath = [directoryEnumerator nextObject]) {
+            [fileManager removeItemAtPath:[NSString stringWithFormat:@"%@/%@", self.cacheDirectoryPath, filePath] error:&error];
+            if (error) {
+                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
+                                                                message:[NSString stringWithFormat:@"Error in removing cached file, %@", error.localizedDescription]
+                                                               delegate:nil
+                                                      cancelButtonTitle:@"OK"
+                                                      otherButtonTitles:nil];
+                [alert show];
+                [alert release];
+            } else {
+                for (NSString *hash in [self.cachedObjectsDictionary allKeys])
+                    if ([[self.cachedObjectsDictionary objectForKey:hash] isEqualToString:filePath])
+                        [self.cachedObjectsDictionary removeObjectForKey:hash];
+            }
+        }
+    }
+    [self saveCacheDictionary];
+}
+
 - (void)saveCacheDictionary {
     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    [userDefaults setObject:cachedObjectsDictionary forKey:@"cachedObjectsDictionary"];
+    [userDefaults setObject:self.cachedObjectsDictionary forKey:@"cachedObjectsDictionary"];
     [userDefaults synchronize];
 }
 
+- (OpenStackRequest *)objectDownloadRequestForAccount:(OpenStackAccount *)account
+                                            container:(Container *)container
+                                               object:(StorageObject *)object {
+    return [[[self.objectDownloadRequests objectForKey:account.uuid] objectForKey:container.name] objectForKey:object.fullPath];
+}
+
+- (void)setObjectDownloadRequest:(OpenStackRequest *)request
+                      forAccount:(OpenStackAccount *)account
+                       container:(Container *)container
+                          object:(StorageObject *)object {
+    NSMutableDictionary *accountObjectDownloadRequests = [self.objectDownloadRequests objectForKey:account.uuid];
+    if (!accountObjectDownloadRequests) {
+        accountObjectDownloadRequests = [NSMutableDictionary dictionary];
+        [self.objectDownloadRequests setObject:accountObjectDownloadRequests forKey:account.uuid];
+    }
+    NSMutableDictionary *containerObjectDownloadRequests = [accountObjectDownloadRequests objectForKey:container.name];
+    if (!containerObjectDownloadRequests) {
+        containerObjectDownloadRequests = [NSMutableDictionary dictionary];
+        [accountObjectDownloadRequests setObject:containerObjectDownloadRequests forKey:container.name];
+    }
+    [containerObjectDownloadRequests setObject:request forKey:object.fullPath];
+}
+
+- (void)removeObjectDownloadRequestForAccount:(OpenStackAccount *)account
+                                    container:(Container *)container
+                                       object:(StorageObject *)object {
+    NSMutableDictionary *accountObjectDownloadRequests = [self.objectDownloadRequests objectForKey:account.uuid];
+    if (accountObjectDownloadRequests) {
+        NSMutableDictionary *containerObjectDownloadRequests = [accountObjectDownloadRequests objectForKey:container.name];
+        if (containerObjectDownloadRequests) {
+            [containerObjectDownloadRequests removeObjectForKey:object.fullPath];
+        }
+    }
+}
+
 #pragma mark - Memory management
 
 - (void)dealloc {
     [rootViewController release];
        [window release];
     [cachedObjectsDictionary release];
+    [objectDownloadRequests release];
        [super dealloc];
 }
 
index bb9087d..46465a8 100755 (executable)
         [self.navigationController pushViewController:vc animated:YES];
         [vc release];
     } else if (indexPath.section == kCache) {
-        OpenStackAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
-        NSFileManager *fileManager = [NSFileManager defaultManager];
-        NSError *error = nil;
-        NSString *file;
-        NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:appDelegate.cacheDirectoryPath];
-        @synchronized(appDelegate.cachedObjectsDictionary) {
-            while (file = [directoryEnumerator nextObject]) {
-                [fileManager removeItemAtPath:[NSString stringWithFormat:@"%@/%@",appDelegate.cacheDirectoryPath,file] error:&error];
-                if (error) {
-                    [self alert:@"Error" message:[NSString stringWithFormat:@"Error in removing cached file, %@", error.localizedDescription]];
-                } else {
-                    for (NSString *key in [appDelegate.cachedObjectsDictionary allKeys])
-                        if ([[appDelegate.cachedObjectsDictionary objectForKey:key] isEqualToString:file])
-                            [appDelegate.cachedObjectsDictionary removeObjectForKey:key];
-                }        
-            }
-        }
+        OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+        [app removeAllCacheObjects];
         [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
         [self.tableView reloadData];
     } else if (indexPath.section == kAbout) {
index f122fb6..3336f1c 100755 (executable)
@@ -9,6 +9,12 @@
 #import "ASIProgressDelegate.h"
 #import <MessageUI/MFMailComposeViewController.h>
 
+enum {
+    StorageObjectActionOpenFile = 0,
+    StorageObjectActionMailFile = 1,
+};
+typedef NSInteger StorageObjectAction;
+
 @class OpenStackAccount, Container, Folder, StorageObject, AnimatedProgressView, FolderViewController;
 
 @interface StorageObjectViewController : UITableViewController <UIDocumentInteractionControllerDelegate, ASIProgressDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate> {
@@ -36,7 +42,6 @@
     NSString *oldObjectSharingString;
     NSString *oldPublicURI;
     NSMutableDictionary *permissions;
-    NSIndexPath *actionSelectedIndexPath;
     NSString *versionID;
 }
 
 @property (nonatomic, retain) FolderViewController *folderViewController;
 @property (nonatomic, retain) NSString *oldPubicURI;
 @property (nonatomic, retain) UIDocumentInteractionController *documentInteractionController;
-@property (nonatomic, retain) NSIndexPath *actionSelectedIndexPath;
 @property (nonatomic, assign) BOOL objectIsReadOnly;
 @property (nonatomic, retain) NSString *versionID;
 
 - (void)setProgress:(float)newProgress;
 - (void)reloadMetadataSection;
+- (void)downloadFileForAction:(StorageObjectAction)action;
 
 @end
index 8f68994..d203288 100755 (executable)
@@ -46,8 +46,18 @@ Size                            123 KB
 Type                            text/plain
 Last Modified                   2012-12-21 11:11:00
 
-Download File (if downloaded, Open File and Mail File as Attachment)
-"After you download the file, you'll be able to attempt to open it and mail is as an attachment."
+if fileDownloaded:
+ Open File
+ Mail File
+ Delete from Cache
+else if fileDownloading:
+ Open File (disabled)
+ Mail File (disabled)
+ Downloading (progress view)
+ Cancel
+else:
+ Open File
+ Mail File
 
 Metadata
 Key                             Value -> tap goes to a metadata item VC to edit or delete
@@ -66,7 +76,7 @@ Delete Object
 @implementation StorageObjectViewController
 
 @synthesize account, container, folder, object, folderViewController;
-@synthesize oldPubicURI, documentInteractionController, actionSelectedIndexPath, objectIsReadOnly, versionID;
+@synthesize oldPubicURI, documentInteractionController, objectIsReadOnly, versionID;
 
 #pragma mark - View lifecycle
 
@@ -104,12 +114,15 @@ Delete Object
     }
     
     downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
-    fileDownloading = ([self.account.manager.objectDownloadRequests objectForKey:object.fullPath] != nil) ? YES : NO;
-    if (fileDownloading) {
-        OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
-        self.actionSelectedIndexPath = [request.userInfo objectForKey:@"actionSelectedIndexPath"];
-        [request setDownloadProgressDelegate:self];
+    fileDownloading = NO;
+    OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+    OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object];
+    if (request) {
+        fileDownloading = YES;
+        request.downloadProgressDelegate = self;
+        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
     }
+    
 }
 
 - (void)viewWillAppear:(BOOL)animated {
@@ -119,15 +132,10 @@ Delete Object
     NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
        if (selection)
                [self.tableView deselectRowAtIndexPath:selection animated:YES];
+    
     OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
-    if ([app.cachedObjectsDictionary objectForKey:object.hash] != nil) {
-        if ([[NSFileManager defaultManager] fileExistsAtPath:[app.cachedObjectsDictionary objectForKey:object.hash]]) {
-            fileDownloaded = YES;
-        } else {
-            fileDownloaded = NO;
-            [app.cachedObjectsDictionary removeObjectForKey:object.hash];
-            [app saveCacheDictionary];
-        }
+    if ([app cacheFilePathForHash:object.hash]) {
+        fileDownloaded = YES;
     } else {
         fileDownloaded = NO;
     }
@@ -164,14 +172,20 @@ Delete Object
     if (object.metadata == nil) {
         [self reloadMetadataSection];
     }
+    if (fileDownloading) {
+        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:kActions]
+                              atScrollPosition:UITableViewScrollPositionMiddle
+                                      animated:YES];
+    }
 }
 
 #pragma mark - Memory management
 
 - (void)dealloc {
     if (fileDownloading) {
-        OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
-        [request setDownloadProgressDelegate:nil];
+        OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+        OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object];
+        request.downloadProgressDelegate = nil;
     }
     [account release];
     [downloadProgressView release];
@@ -180,7 +194,6 @@ Delete Object
     [objectIsPublicSwitch release];
     [permissions release];
     [documentInteractionController release];
-    [actionSelectedIndexPath release];
     [versionID release];
     [super dealloc];
 }
@@ -232,6 +245,59 @@ Delete Object
      }];
 }
 
+- (void)openFile {
+    OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+    NSString *filePath = [app cacheFilePathForHash:object.hash];
+    self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
+    self.documentInteractionController.delegate = self;
+    self.documentInteractionController.name = object.name;
+    
+    CGRect frameToPresentMenuFrom;
+    if ([[UIDevice currentDevice].systemVersion hasPrefix:@"6"]) {
+        frameToPresentMenuFrom = CGRectMake(0.0,
+                                            self.tableView.contentOffset.y,
+                                            self.view.frame.size.width,
+                                            1.0);
+    } else {
+        UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kActions]];
+        frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
+                                            openFileCell.frame.origin.y - self.tableView.contentOffset.y,
+                                            openFileCell.frame.size.width,
+                                            openFileCell.frame.size.height);
+    }
+    if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
+        if ([self.object isPlayableMedia]) {
+            MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
+            vc.container = self.container;
+            vc.object = self.object;
+            [self.navigationController pushViewController:vc animated:YES];
+            [vc release];
+        } else {
+            [self alert:@"Error" message:@"This file could not be opened."];
+        }
+    }
+}
+
+- (void)mailFile {
+    if (![MFMailComposeViewController canSendMail]) {
+        [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending mail"];
+    } else {
+        OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+        NSString *filePath = [app cacheFilePathForHash:object.hash];
+        NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
+        
+        MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
+        vc.mailComposeDelegate = self;
+        [vc setSubject:object.name];
+        [vc addAttachmentData:data mimeType:object.contentType fileName:object.name];
+        [vc setMessageBody:@"" isHTML:NO];
+        if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
+            vc.modalPresentationStyle = UIModalPresentationPageSheet;
+        [self presentModalViewController:vc animated:YES];
+        [vc release];
+    }
+}
+
 #pragma mark - Actions
 
 - (void)reloadMetadataSection {
@@ -259,6 +325,44 @@ Delete Object
      }];
 }
 
+- (void)downloadFileForAction:(StorageObjectAction)action {
+    if (fileDownloading)
+        return;
+    fileDownloading = YES;
+    [downloadProgressView setProgress:0.0 animated:NO];
+    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:kActions]
+                          atScrollPosition:UITableViewScrollPositionMiddle
+                                  animated:YES];
+    NSMutableDictionary *requestUserInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:action] forKey:@"action"];
+    [[self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self requestUserInfo:requestUserInfo version:versionID]
+     success:^(OpenStackRequest *request) {
+         if (request.isCancelled) {
+             fileDownloaded = NO;
+             fileDownloading = NO;
+             [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+         } else {
+             fileDownloaded = YES;
+             fileDownloading = NO;
+             if ([self.navigationController.visibleViewController isEqual:self]) {
+                 if ([[request.userInfo objectForKey:@"action"] integerValue] == StorageObjectActionMailFile) {
+                     [self mailFile];
+                 } else {
+                     [self openFile];
+                 }
+             }
+             [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+             [self.folderViewController reloadData];
+         }
+     }
+     failure:^(OpenStackRequest *request) {
+         fileDownloaded = NO;
+         fileDownloading = NO;
+         [self alert:@"File failed to download." request:request];
+         [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+     }];
+}
+
 #pragma mark - UITableViewDataSource
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
@@ -279,7 +383,13 @@ Delete Object
     if (section == kDetails) {
         return (object.lastModifiedString ? 5: 4);
     } else if (section == kActions) {
-        return 2;
+        if (fileDownloaded) {
+            return 3;
+        } else if (fileDownloading) {
+            return 4;
+        } else {
+            return 2;
+        }
     } else if (section == kMetadata) {
         if (objectIsReadOnly) {
             return [object.metadata count];
@@ -466,26 +576,44 @@ Delete Object
             cell.detailTextLabel.text = accessType;
         }
     } else if (indexPath.section == kActions) {
-        if (fileDownloading) {
-            if (actionSelectedIndexPath.row == indexPath.row) {
-                cell.accessoryView = downloadProgressView;
-            } else {
-                cell.textLabel.textColor = [UIColor grayColor];
-                cell.selectionStyle = UITableViewCellSelectionStyleNone;
-            }
-        } else {
-            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
-            cell.accessoryView = nil;
-        }
         if (indexPath.row == 0) {
-            cell.textLabel.text = @"Open File"; 
+            cell.textLabel.text = @"Open File";
             cell.accessoryType = UITableViewCellAccessoryNone;
             cell.detailTextLabel.text = @"";
-            
+            if (fileDownloading) {
+                cell.textLabel.textColor = [UIColor grayColor];
+                cell.selectionStyle = UITableViewCellSelectionStyleNone;
+            } else {
+                cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            }
         } else if (indexPath.row == 1) {
             cell.textLabel.text = @"Email File as Attachment";
             cell.detailTextLabel.text = @"";
-            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+            cell.accessoryType = UITableViewCellAccessoryNone;
+            if (fileDownloading) {
+                cell.textLabel.textColor = [UIColor grayColor];
+                cell.selectionStyle = UITableViewCellSelectionStyleNone;
+            } else {
+                cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+            }
+        } else if (indexPath.row == 2) {
+            if (fileDownloaded) {
+                cell.textLabel.text = @"Remove from Cache";
+                cell.accessoryType = UITableViewCellAccessoryNone;
+                cell.detailTextLabel.text = @"";
+            } else if (fileDownloading) {
+                cell.textLabel.text = @"Downloading";
+                cell.accessoryType = UITableViewCellAccessoryNone;
+                cell.detailTextLabel.text = @"";
+                cell.selectionStyle = UITableViewCellSelectionStyleNone;
+                cell.accessoryView = downloadProgressView;
+            }
+        } else if (indexPath.row == 3) {
+            if (fileDownloading) {
+                cell.textLabel.text = @"Cancel";
+                cell.accessoryType = UITableViewCellAccessoryNone;
+                cell.detailTextLabel.text = @"";
+            }
         }
     } else if (indexPath.section == versionsSection) {
         cell.selectionStyle = UITableViewCellSelectionStyleBlue;
@@ -558,88 +686,41 @@ Delete Object
         [self.navigationController pushViewController:vc animated:YES];
         [vc release];
     } else if (indexPath.section == kActions) {
-        if (!fileDownloaded) {
-            if (!fileDownloading) {
-                // download the file
-                fileDownloading = YES;
-                self.actionSelectedIndexPath = indexPath;
-                [self.tableView reloadData];
-                NSMutableDictionary *requestUserInfo = [NSDictionary dictionaryWithObject:indexPath forKey:@"actionSelectedIndexPath"];
-                [[self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self requestUserInfo:requestUserInfo version:versionID]
-                 success:^(OpenStackRequest *request) {
-                     fileDownloaded = YES;
-                     fileDownloading = NO;
-                     if ([self.navigationController.visibleViewController isEqual:self])
-                         [self.tableView.delegate tableView:self.tableView didSelectRowAtIndexPath:indexPath]; 
-                     [self.tableView reloadData];
-                     [self.folderViewController reloadData];
-                 }
-                 failure:^(OpenStackRequest *request) {
-                     fileDownloaded = NO;
-                     fileDownloading = NO;
-                     [self alert:@"File failed to download." request:request];
-                     [self.tableView reloadData];
-                 }];
-                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:kActions]] withRowAnimation:UITableViewRowAnimationNone];
-            }
-        } else if (indexPath.row == 0) {        
+        if (indexPath.row == 0) {
             if (fileDownloaded) {
-                OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
-                NSString *filePath = [app.cachedObjectsDictionary objectForKey:object.hash];
-                self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
-                self.documentInteractionController.delegate = self;
-                self.documentInteractionController.name = object.name;
-
-                UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
-                CGRect frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
-                                                           openFileCell.frame.origin.y - self.tableView.contentOffset.y,
-                                                           openFileCell.frame.size.width, 
-                                                           openFileCell.frame.size.height);
-                if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
-                    if ([self.object isPlayableMedia]) {
-                        MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
-                        vc.container = self.container;
-                        vc.object = self.object;
-                        [self.navigationController pushViewController:vc animated:YES];
-                        [vc release];
-                    } else {
-                        [self alert:@"Error" message:@"This file could not be opened."];
-                        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
-                    }
-                } 
-                
-                [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kActions] animated:YES];
-                
-            } 
+                [self openFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            } else if (!fileDownloading) {
+                [self downloadFileForAction:StorageObjectActionOpenFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            }
         } else if (indexPath.row == 1) {
-            
-            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-            NSString *documentsDirectory = [paths objectAtIndex:0];        
-            NSString *shortPath = @"";
-            
-            if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
-                shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
+            if (fileDownloaded) {
+                [self mailFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            } else if (!fileDownloading) {
+                [self downloadFileForAction:StorageObjectActionMailFile];
+                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
             }
-            
-            NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
-            NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
-            
-            
-            if (![MFMailComposeViewController canSendMail]) {
-                [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending email"];
-                [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
-            } else {
-                MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
-            
-                vc.mailComposeDelegate = self;         
-                [vc setSubject:self.object.name];
-                [vc addAttachmentData:data mimeType:self.object.contentType fileName:self.object.name];
-                [vc setMessageBody:@"" isHTML:NO];    
-                if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
-                    vc.modalPresentationStyle = UIModalPresentationPageSheet;
-                }                
-                [self presentModalViewController:vc animated:YES];
-                [vc release];        
+        } else if ((indexPath.row == 2) && fileDownloaded) {
+            OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+            BOOL removed = [app removeCacheObjectForHash:object.hash];
+            [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            if (removed) {
+                fileDownloaded = NO;
+                fileDownloading = NO;
+                [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
+            }
+        } else if ((indexPath.row == 3) && fileDownloading) {
+            OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
+            OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object];
+            [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+            if (request) {
+                request.downloadProgressDelegate = nil;
+                [request cancel];
+                fileDownloaded = NO;
+                fileDownloading = NO;
+                [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
             }
         }
     } else if (indexPath.section == deleteSection) {
@@ -664,7 +745,7 @@ Delete Object
     if (newProgress >= 1.0) {
         fileDownloading = NO;
         fileDownloaded = YES;
-        [self.tableView reloadData];
+        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone];
     }
 }
 
@@ -718,9 +799,8 @@ Delete Object
 #pragma mark - MFMailComposeViewControllerDelegate
 
 // Dismisses the email composition interface when users tap Cancel or Send.
-- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { 
+- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
        [self dismissModalViewControllerAnimated:YES];
-    [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:kActions] animated:YES];
 }
 
 @end
index 1d12d86..30d6a6d 100755 (executable)
@@ -43,7 +43,7 @@
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
-       <string>1.1.5</string>
+       <string>1.1.6</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleURLTypes</key>
@@ -58,7 +58,7 @@
                </dict>
        </array>
        <key>CFBundleVersion</key>
-       <string>20121220.0</string>
+       <string>20121228.0</string>
        <key>LSRequiresIPhoneOS</key>
        <true/>
        <key>NSMainNibFile</key>