2
// Copyright 2011 Canonical Ltd.
4
// This program is free software: you can redistribute it and/or modify it
5
// under the terms of the GNU Affero General Public License version 3,
6
// as published by the Free Software Foundation.
8
// This program is distributed in the hope that it will be useful, but
9
// WITHOUT ANY WARRANTY; without even the implied warranties of
10
// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
// PURPOSE. See the GNU Affero General Public License for more details.
13
// You should have received a copy of the GNU Affero General Public License
14
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
#import "U1LocalAssetsViewController.h"
18
#import "U1ByteSizeValueTransformer.h"
19
#import "U1FileNode.h"
20
#import "U1FilePreviewViewController.h"
21
#import "U1FilesClient.h"
22
#import "U1FolderItemCell.h"
23
#import "U1FolderNode.h"
25
#import "UIActionSheet+U1Additions.h"
26
#import "U1LocalAssetsManager.h"
27
#import "U1DataRepository.h"
29
@interface U1LocalAssetsViewController () <U1LocalAssetsViewControllerDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, NSFetchedResultsControllerDelegate>
30
@property (copy) NSString *resourcePath;
31
@property (retain) U1ByteSizeValueTransformer *byteSizeTransformer;
32
@property (retain) NSIndexPath *pendingIndexPath;
33
@property (retain) U1FolderNode *node;
34
@property (retain) NSMutableArray *nodes;
35
@property (retain) NSFetchedResultsController *resultsController;
36
@property (retain) U1LocalAssetsManager *localAssetsManager;
37
- (void)updateFeedback:(id)sender;
38
- (void)loadInfoForNode;
42
@implementation U1LocalAssetsViewController
44
@synthesize loadingCell, progressView, progressBar, progressLabel;
45
@synthesize delegate, filesClient, node, resourcePath;
46
@synthesize byteSizeTransformer, pendingIndexPath, nodes, resultsController, localAssetsManager;
48
- (id)initWithNodeResourcePath:(NSString*)theResourcePath;
50
self = [super initWithNibName:@"U1LocalAssetsViewController" bundle:nil];
53
self.title = NSLocalizedString(@"Camera Roll", @"");
54
self.resourcePath = theResourcePath;
55
self.byteSizeTransformer = [[U1ByteSizeValueTransformer new] autorelease];
56
self.delegate = self; // ???
57
filesClient = [U1FilesClient sharedFilesClient];
58
localAssetsManager = [U1LocalAssetsManager sharedLocalAssetsManager];
60
// This button should use a camera icon and go to the image picker to take a new photo.
61
UIBarButtonItem *cameraButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:@selector(takePicture:)];
62
[self.navigationItem setRightBarButtonItem:cameraButton];
63
[cameraButton release];
65
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
67
[titleLabel setFont:[UIFont boldSystemFontOfSize:18.f]];
68
[titleLabel setTextColor:[UIColor blackColor]];
69
[titleLabel setShadowColor:[UIColor whiteColor]];
70
[titleLabel setShadowOffset:CGSizeMake(-1, -1)];
71
[titleLabel setText:self.title];
72
[titleLabel sizeToFit];
73
[titleLabel setOpaque:NO];
74
[titleLabel setBackgroundColor:[UIColor clearColor]];
75
[self.navigationItem setTitleView:titleLabel];
77
// [uploadItem release];
84
[loadingCell release];
85
[resourcePath release];
87
[byteSizeTransformer release];
88
[pendingIndexPath release];
90
[resultsController release];
97
[self updateFeedback:nil];
99
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateFeedback:) name:@"imageUploaded" object:nil];
100
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
101
[longPress setMinimumPressDuration:1.f];
102
[self.tableView addGestureRecognizer:longPress];
106
- (void)viewDidUnload;
108
[loadingCell release], loadingCell = nil;
109
[[NSNotificationCenter defaultCenter] removeObserver:self];
110
[super viewDidUnload];
113
- (void)viewDidAppear:(BOOL)animated;
115
[super viewDidAppear:animated];
116
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
119
- (void)longPress:(UILongPressGestureRecognizer*)longPress;
121
if (longPress.state == UIGestureRecognizerStateBegan)
123
CGPoint p = [longPress locationInView:self.tableView];
124
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
125
U1Node *childNode = [self.nodes objectAtIndex:indexPath.row];
127
UIActionSheet *actionSheet = [UIActionSheet new];
129
[actionSheet setTitle:[[childNode path] lastPathComponent]];
131
NSInteger publishButtonIndex = -1;
132
NSInteger copyURLButtonIndex = -1;
133
if ([childNode isFile])
135
U1FileNode *fileNode = (id)childNode;
136
if ([[fileNode isPublic] boolValue])
138
publishButtonIndex = [actionSheet addButtonWithTitle:@"Unpublish"];
139
copyURLButtonIndex = [actionSheet addButtonWithTitle:@"Copy Public URL"];
143
publishButtonIndex = [actionSheet addButtonWithTitle:@"Publish"];
146
[actionSheet setCancelButtonIndex:[actionSheet addButtonWithTitle:@"Cancel"]];
148
self.pendingIndexPath = indexPath;
149
[actionSheet showInView:self.view withCompletionBlock:^(NSInteger buttonIndex) {
150
if (buttonIndex == publishButtonIndex)
152
U1FileNode *fileNode = (id)childNode;
153
if ([[fileNode isPublic] boolValue])
154
[self.filesClient unpublishNode:fileNode completionBlock:^(U1FileNode *updatedNode, NSError *error) {
157
[self.filesClient publishNode:fileNode completionBlock:^(U1FileNode *updatedNode, NSError *error) {
160
else if (buttonIndex == copyURLButtonIndex)
162
U1FileNode *fileNode = (id)childNode;
163
NSArray *types = UIPasteboardTypeListString;
164
[[UIPasteboard generalPasteboard] setValue:[fileNode publicURLString] forPasteboardType:[types lastObject]];
166
else if (buttonIndex == [actionSheet cancelButtonIndex])
170
self.pendingIndexPath = nil;
175
- (void)takePicture:(id)sender;
177
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
178
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
179
imagePicker.delegate = self;
180
[self presentModalViewController:imagePicker animated:YES];
183
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info;
185
// TODO: check for jpg/png
186
// TODO: handle videos (stream)
188
[picker dismissModalViewControllerAnimated:YES];
190
// NSURL *imageURL = [info objectForKey:UIImagePickerControllerReferenceURL];
191
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
193
UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
196
- (void)updateFeedback:(id)sender;
198
// Update the label and the progress bar
199
// progressLabel = (numberOfAssets - assetsPendingUpload) / numberOfAssets
200
dispatch_async(dispatch_get_main_queue(), ^{
201
U1LocalAssetsManager *localAssets = [U1LocalAssetsManager sharedLocalAssetsManager];
202
int totalAssets = [localAssets numberOfAssets];
203
int assetsUploaded = [localAssets numberOfAssetsUploaded];
204
float percentDone = (float)assetsUploaded / (float)totalAssets;
205
self.progressLabel.text = [NSString stringWithFormat:@"%d of %d uploaded", assetsUploaded, totalAssets];
206
self.progressBar.progress = percentDone;
207
[self.tableView reloadData];
211
#pragma mark UITableViewDataSource Methods
213
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
215
if (!self.resultsController)
218
return [[[self.resultsController sections] objectAtIndex:section] numberOfObjects];
221
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
223
if (section == 0 && [self.localAssetsManager.localAssetsToUpload count] > 0)
228
return [tableView sectionHeaderHeight];
231
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
233
if (section == 0 && [self.localAssetsManager.localAssetsToUpload count] > 0)
235
return self.progressView;
241
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
243
if (!self.resultsController)
245
return self.loadingCell;
248
U1FolderItemCell *cell = [U1FolderItemCell cellForTableView:tableView];
250
U1Node *childNode = [self.resultsController objectAtIndexPath:indexPath];
251
cell.nameLabel.text = [childNode.resourcePath lastPathComponent];
252
NSString *detailString = @"Pending";
253
// TODO: get this thumbnail loading happening inside an operation queue, tied to visible rows
254
// [self.localAssetsManager thumbnailForNode:childNode completionBlock:^(CGImageRef thumbnail) {
255
// cell.iconImageView.image = [UIImage imageWithCGImage:thumbnail];
258
BOOL hasChildren = [[childNode hasChildren] boolValue];
259
[cell setAccessoryType:(hasChildren ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone)];
261
if (childNode.generation == nil)
263
cell.nameLabel.textColor = [UIColor grayColor];
264
// query the local assets manager to see if this childNode's corresponding asset is actually in the upload operation queue
265
BOOL isUploading = [self.localAssetsManager isResourceUploading:childNode.resourcePath];
268
[cell.uploadIndicator setHidden:NO];
269
[cell.uploadIndicator startAnimating];
270
detailString = @"Uploading";
273
[cell.uploadIndicator setHidden:YES];
274
[cell.uploadIndicator stopAnimating];
279
cell.nameLabel.textColor = [UIColor blackColor];
280
[cell.detailLabel setHidden:NO];
281
[cell.uploadIndicator setHidden:YES];
282
[cell.uploadIndicator stopAnimating];
285
[cell.imageView setImage:nil];
286
if ([childNode isFolder])
288
[cell.imageView setImage:[UIImage imageNamed:@"ic_folder"]];
289
detailString = [childNode.modificationDate description];
293
NSString *contentPath = childNode.contentPath;
294
NSURL *contentURL = [NSURL fileURLWithPath:contentPath];
295
UIDocumentInteractionController *docController = [UIDocumentInteractionController interactionControllerWithURL:contentURL];
296
[cell.imageView setImage:[docController.icons objectAtIndex:0]];
297
NSNumber *size = [childNode valueForKey:@"size"];
298
NSString *sizeString = [self.byteSizeTransformer transformedValue:size];
299
if (childNode.generation != nil)
301
detailString = [sizeString stringByAppendingFormat:@", modified %@", childNode.modificationDate];
305
[cell.detailLabel setText:detailString];
310
#pragma mark UITableViewDelegate Methods
312
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
314
U1Node *childNode = [self.resultsController objectAtIndexPath:indexPath];
315
[self.delegate localAssetsController:self didSelectNode:childNode];
320
[self loadInfoForNode];
324
#pragma mark Private Methods
326
- (void)loadInfoForNode;
328
NSParameterAssert(self.resourcePath != nil);
330
[self.filesClient fetchFolderWithResourcePath:self.resourcePath completionBlock:^(U1Node *theNode, NSFetchedResultsController *childrenResultsController, NSError *error) {
332
self.node = (id)theNode;
333
self.resultsController = childrenResultsController;
334
[self.resultsController setDelegate:self];
336
[self.tableView reloadData];
340
#pragma mark NSFetchedResultsControllerDelegate Methods
343
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;
345
[self.tableView beginUpdates];
349
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
350
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type;
354
case NSFetchedResultsChangeInsert:
355
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
356
withRowAnimation:UITableViewRowAnimationFade];
359
case NSFetchedResultsChangeDelete:
360
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
361
withRowAnimation:UITableViewRowAnimationFade];
366
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
367
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
368
newIndexPath:(NSIndexPath *)newIndexPath;
371
UITableView *tableView = self.tableView;
376
case NSFetchedResultsChangeInsert:
377
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
378
withRowAnimation:UITableViewRowAnimationFade];
381
case NSFetchedResultsChangeDelete:
382
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
383
withRowAnimation:UITableViewRowAnimationFade];
386
case NSFetchedResultsChangeUpdate:
388
// [self configureCell:[tableView cellForRowAtIndexPath:indexPath]
389
// atIndexPath:indexPath];
390
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
391
withRowAnimation:UITableViewRowAnimationNone];
394
case NSFetchedResultsChangeMove:
395
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
396
withRowAnimation:UITableViewRowAnimationFade];
397
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
398
withRowAnimation:UITableViewRowAnimationFade];
403
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;
405
[self.tableView endUpdates];
408
#pragma mark U1LocalAssetsViewControllerDelegate Methods
410
- (void)localAssetsController:(U1LocalAssetsViewController *)localAssetsController didSelectNode:(U1Node *)aNode
414
U1FilePreviewViewController *fileController = [[U1FilePreviewViewController alloc] initWithNode:(id)node];
415
[self.navigationController pushViewController:fileController animated:YES];
416
[fileController release];