Merge branch 'master' of https://code.grnet.gr/git/pithos-ios
[pithos-ios] / Classes / AddPhotoViewController.m
1 //
2 //  AddPhotoViewController.m
3 //  OpenStack
4 //
5 //  Created by Mike Mayo on 1/5/11.
6 //  Copyright 2011 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "AddPhotoViewController.h"
10 #import "UIViewController+Conveniences.h"
11 #import "UIColor+MoreColors.h"
12 #import "Container.h"
13 #import "ActivityIndicatorView.h"
14 #import "StorageObject.h"
15 #import "OpenStackAccount.h"
16 #import "AccountManager.h"
17 #import "Container.h"
18 #import "Folder.h"
19 #import "FolderViewController.h"
20 #import "NSObject+Conveniences.h"
21 #import "APICallback.h"
22 #import "ComputeModel.h"
23
24 #define kName 0
25
26 // give me the option to add a description of the file and the URL - date/time stamped
27
28 @implementation AddPhotoViewController
29
30 @synthesize image, account, container, folder, folderViewController, isFromCamera;
31
32 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
33     return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
34 }
35
36 #pragma mark -
37 #pragma mark View lifecycle
38
39 - (void)viewDidLoad {
40     [super viewDidLoad];
41     self.navigationItem.title = @"Add Photo";
42     [self addSaveButton];
43     format = @".jpg";
44     
45     if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
46         nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(98.0, 13.0, 400.0, 24.0)];
47     } else {
48         nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(79.0, 13.0, 222.0, 24.0)];
49     }    
50     nameTextField.delegate = self;
51     nameTextField.font = [UIFont systemFontOfSize:17.0];
52     nameTextField.textColor = [UIColor value1DetailTextLabelColor];
53     nameTextField.backgroundColor = [UIColor clearColor];
54     nameTextField.textAlignment = UITextAlignmentRight;
55     nameTextField.returnKeyType = UIReturnKeyDone;
56     nameTextField.autocorrectionType = UITextAutocorrectionTypeNo;
57     nameTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;    
58     nameTextField.placeholder = [NSString stringWithFormat:@"ios_upload_%.0f", [[NSDate date] timeIntervalSince1970]];    
59     
60     if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
61         formatLabel = [[UILabel alloc] initWithFrame:CGRectMake(41.0, 15.5, 458.0, 18.0)];
62     } else {
63         formatLabel = [[UILabel alloc] initWithFrame:CGRectMake(21.0, 15.5, 280.0, 18.0)];
64     }    
65     formatLabel.font = [UIFont systemFontOfSize:17.0];
66     formatLabel.textColor = [UIColor value1DetailTextLabelColor];
67     formatLabel.backgroundColor = [UIColor clearColor];
68     formatLabel.textAlignment = UITextAlignmentRight;
69     formatLabel.text = format;
70     
71     slider = [[UISlider alloc] init];
72     slider.value = 0.65;
73     
74     // move the text field to make room for the numbers label
75     CGSize size = [formatLabel.text sizeWithFont:formatLabel.font constrainedToSize:CGSizeMake(280.0, 900.0f)];
76     if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
77         nameTextField.frame = CGRectMake(98.0, 13.0, 400.0 - size.width, 24.0);
78     } else {
79         nameTextField.frame = CGRectMake(79.0, 13.0, 222.0 - size.width, 24.0);
80     }    
81     
82 }
83
84 - (void)viewWillAppear:(BOOL)animated {
85     [super viewWillAppear:animated];
86 //    data = UIImageJPEGRepresentation(image, slider.value);
87 //    [data retain];
88     
89     // when saving a camera image as a PNG, it ends up rotated.  i have no idea why and have
90     // not been able to successfully rotate the image, so we're only allowing JPEG from the
91     // camera for now.  not a big deal, since the iPhone 4 camera is making 8 MB PNG files
92     if (isFromCamera) {
93         formatSection = -1;
94         qualitySection = 1;
95     } else {
96         formatSection = 1;
97         qualitySection = 2;
98     }
99 }
100
101 - (void)viewDidAppear:(BOOL)animated {
102     [super viewDidAppear:animated];
103     [qualityActivityIndicatorView release];
104     qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithText:@"Calculating size..."];
105     [qualityActivityIndicatorView addToView:self.view];    
106     [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO];
107 }
108
109 #pragma mark -
110 #pragma mark Table view data source
111
112 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
113     if ([format isEqualToString:@".jpg"]) {
114         if (transitioning) {
115             return isFromCamera ? 1 : 2;
116         } else {
117             return isFromCamera ? 2 : 3;
118         }
119     } else {
120         return transitioning ? 3 : 2;
121     }
122 }
123
124 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
125     if (section == kName) {
126         return 2;
127     } else if (section == formatSection) {
128         return 2;
129     } else if (section == qualitySection) {
130         return 1;
131     } else {
132         return 0;
133     }
134 }
135
136 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
137     if (indexPath.section == qualitySection) {
138         return tableView.rowHeight + slider.frame.size.height + 3.0;
139     } else {
140         return tableView.rowHeight;
141     }
142 }
143 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
144     if (section == formatSection) {
145         return @"JPEG is a lossy format designed for digital photography. PNG is a lossless format that supports transparency.";
146     } else if (section == qualitySection) {
147         return @"A high quality will produce a better image. A low quality will use less space.";
148     } else {
149         return @"";
150     }
151 }
152
153 - (UITableViewCell *)tableView:(UITableView *)tableView nameCellForRowAtIndexPath:(NSIndexPath *)indexPath {
154     static NSString *CellIdentifier = @"NameCell";    
155     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
156     if (cell == nil) {
157         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
158         cell.selectionStyle = UITableViewCellSelectionStyleNone;
159         cell.textLabel.text = @"Name";
160         [cell addSubview:nameTextField];
161         [cell addSubview:formatLabel];
162     }    
163     return cell;
164 }
165
166 - (UITableViewCell *)tableView:(UITableView *)tableView qualityCellForRowAtIndexPath:(NSIndexPath *)indexPath {
167     static NSString *CellIdentifier = @"QualityCell";
168     
169     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
170     if (cell == nil) {
171         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
172         cell.selectionStyle = UITableViewCellSelectionStyleNone;
173         
174         UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 13.0, 280.0, 20.0)];
175         if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
176             textLabel.frame = CGRectMake(41.0, 13.0, 458.0, 20.0);
177         }
178         textLabel.font = [UIFont boldSystemFontOfSize:17.0];
179         textLabel.text = @"Quality";
180         textLabel.textColor = [UIColor blackColor];
181         textLabel.backgroundColor = [UIColor clearColor];
182         [cell addSubview:textLabel];
183         [textLabel release];
184         
185         if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
186             slider.frame = CGRectMake(41.0, 38.0, 458.0, slider.frame.size.height);
187         } else {
188             slider.frame = CGRectMake(20.0, 38.0, 280.0, slider.frame.size.height);
189         }
190         
191         [slider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventValueChanged];
192         [slider addTarget:self action:@selector(sliderFinished:) forControlEvents:UIControlEventTouchUpInside];
193         
194         [cell addSubview:slider];
195         qualityLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 14.0, 280.0, 18.0)];
196         if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
197             qualityLabel.frame = CGRectMake(41.0, 14.0, 458.0, 18.0);
198         }        
199         qualityLabel.font = [UIFont systemFontOfSize:17.0];
200         qualityLabel.textColor = [UIColor value1DetailTextLabelColor];
201         qualityLabel.backgroundColor = [UIColor clearColor];
202         qualityLabel.textAlignment = UITextAlignmentRight;
203         [cell addSubview:qualityLabel];
204     }
205     
206     qualityLabel.text = [NSString stringWithFormat:@"%.0f%%", slider.value * 100];
207     
208     return cell;
209 }
210
211 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
212
213     if (indexPath.section == kName && indexPath.row == 0) {
214         return [self tableView:tableView nameCellForRowAtIndexPath:indexPath];
215     } else if (indexPath.section == qualitySection) {
216         return [self tableView:tableView qualityCellForRowAtIndexPath:indexPath];
217     } else {
218         static NSString *CellIdentifier = @"Cell";
219         
220         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
221         if (cell == nil) {
222             cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
223         }
224         
225         if (indexPath.section == kName) {
226             cell.textLabel.text = @"Size";
227             cell.detailTextLabel.text = [Container humanizedBytes:[data length]];
228             cell.selectionStyle = UITableViewCellSelectionStyleNone;
229             cell.accessoryType = UITableViewCellAccessoryNone;
230         } else if (indexPath.section == formatSection) {
231             cell.selectionStyle = UITableViewCellSelectionStyleBlue;
232             if (indexPath.row == 0) {
233                 cell.textLabel.text = @"JPEG";
234                 cell.detailTextLabel.text = @"";
235                 if ([format isEqualToString:@".jpg"]) {
236                     cell.accessoryType = UITableViewCellAccessoryCheckmark;
237                 } else {
238                     cell.accessoryType = UITableViewCellAccessoryNone;
239                 }
240             } else if (indexPath.row == 1) {
241                 cell.textLabel.text = @"PNG";
242                 cell.detailTextLabel.text = @"";
243                 if ([format isEqualToString:@".png"]) {
244                     cell.accessoryType = UITableViewCellAccessoryCheckmark;
245                 } else {
246                     cell.accessoryType = UITableViewCellAccessoryNone;
247                 }
248             }
249         } else {
250             cell.textLabel.text = @"Quality";
251             cell.detailTextLabel.text = @"65%";
252             cell.selectionStyle = UITableViewCellSelectionStyleNone;
253         }
254         
255         return cell;
256     }
257 }
258
259 #pragma mark -
260 #pragma mark Table view delegate
261
262 - (void)updateFormat {
263     
264     transitioning = YES;
265     
266     NSArray *indexPaths = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:formatSection], [NSIndexPath indexPathForRow:1 inSection:formatSection], nil];
267     [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
268     
269     transitioning = NO;
270     
271     if ([format isEqualToString:@".jpg"]) {
272         [self.tableView insertSections:[NSIndexSet indexSetWithIndex:qualitySection] withRowAnimation:UITableViewRowAnimationBottom];
273     } else {
274         [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:qualitySection] withRowAnimation:UITableViewRowAnimationTop];
275     }
276     
277     formatLabel.text = format;
278     // move the text field to make room for the numbers label
279     CGSize size = [formatLabel.text sizeWithFont:formatLabel.font constrainedToSize:CGSizeMake(280.0, 900.0f)];
280     nameTextField.frame = CGRectMake(79.0, 13.0, 222.0 - size.width, 24.0);
281     [qualityActivityIndicatorView release];
282     qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithText:@"Calculating size..."];
283     [qualityActivityIndicatorView addToView:self.view];    
284     [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO];
285     
286 }
287
288 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
289     if (indexPath.section == formatSection) {
290         if (indexPath.row == 0) {
291             format = @".jpg";
292         } else { //if (indexPath.row == 1) {
293             format = @".png";
294         }
295 //        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
296         [NSTimer scheduledTimerWithTimeInterval:0.35 target:self selector:@selector(updateFormat) userInfo:nil repeats:NO];
297     }
298 }
299
300 #pragma mark -
301 #pragma mark Text Field Delegate
302
303 - (BOOL)textFieldShouldReturn:(UITextField *)textField {
304     [textField resignFirstResponder];
305     return NO;
306 }
307
308 #pragma mark -
309 #pragma mark Slider
310
311 - (void)calculateSize {
312     if ([format isEqualToString:@".jpg"]) {
313         [data release];
314         data = UIImageJPEGRepresentation(image, slider.value);
315         [data retain];
316     } else {
317         [data release];
318         
319         if (isFromCamera) {
320             // PNG files from the camera need to be rotated
321             UIImage *rotatedImage = [[UIImage alloc] initWithCGImage:(CGImageRef)image scale:1.0 orientation:UIImageOrientationLeft];
322             data = UIImagePNGRepresentation(rotatedImage);
323             [rotatedImage release];
324         } else {
325             data = UIImagePNGRepresentation(image);
326         }
327         [data retain];
328     }
329     
330     [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:kName]] withRowAnimation:UITableViewRowAnimationNone];    
331     [qualityActivityIndicatorView removeFromSuperview];
332 }
333
334 - (void)sliderFinished:(id)sender {
335     [qualityActivityIndicatorView release];
336     qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithText:@"Calculating size..."];
337     [qualityActivityIndicatorView addToView:self.view];    
338     [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO];
339 }
340
341 - (void)sliderMoved:(id)sender {
342     qualityLabel.text = [NSString stringWithFormat:@"%.0f%%", slider.value * 100];
343 }
344
345 #pragma mark -
346 #pragma mark Save Button
347
348 - (void)saveButtonPressed:(id)sender {
349     [nameTextField resignFirstResponder];
350     NSString *objectName;
351     if (nameTextField.text && ![nameTextField.text isEqualToString:@""]) {
352         objectName = [NSString stringWithFormat:@"%@%@", nameTextField.text, format];
353     } else {
354         objectName = [NSString stringWithFormat:@"%@%@", nameTextField.placeholder, format];
355     }
356     
357     BOOL objectNameHasTrailingSlash = [objectName hasSuffix:@"/"];
358     NSUInteger indexOfFirstSlash = [objectName rangeOfString:@"/"].location;
359     
360     if ((!objectNameHasTrailingSlash && indexOfFirstSlash != NSNotFound) || 
361         (objectNameHasTrailingSlash && indexOfFirstSlash < objectName.length - 1)) {
362         [self alert:@"Invalid file name" message:@"'/' characters are only allowed at the end of file names"];
363         return;
364     }
365     
366     if ([folderViewController.folder.objects objectForKey:objectName] && !allowOverwrite) {
367         NSString *alertMessage = [NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?",objectName, folderViewController.container.name];
368         NSString *alertTitle = @"Apply changes";
369         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertTitle
370                                                         message:alertMessage
371                                                        delegate:self
372                                               cancelButtonTitle:@"Cancel"
373                                               otherButtonTitles:@"OK", nil];
374         [alert show];
375         [alert release];
376         return;
377     }
378
379     StorageObject *object = [[StorageObject alloc] init];
380     object.name = objectName;
381     object.fullPath = [NSString stringWithFormat:@"%@/%@", [folder fullPath], object.name];
382     object.fullPath = [object.fullPath substringFromIndex:1];
383     if ([format isEqualToString:@".jpg"]) {
384         object.contentType = @"image/jpeg";
385     } else {
386         object.contentType = @"image/png";
387     }
388     object.data = data;
389     object.bytes = [data length];
390     
391     __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Uploading..."
392                                                                                                    andAddToView:self.view];
393     [[self.account.manager writeObject:self.container object:object downloadProgressDelegate:activityIndicatorView.progressView]
394      success:^(OpenStackRequest *request) {
395          [activityIndicatorView removeFromSuperview];
396          object.data = nil;
397          object.sharing = folder.sharing;
398          object.lastModified = [ComputeModel dateFromRFC1123String:[request.responseHeaders objectForKey:@"Date"]];
399          BOOL currentFolderIsRoot = NO;
400          if ([folderViewController.folder isEqual:container.rootFolder]) {
401              currentFolderIsRoot = YES;
402          }
403          [folder addObject:object];
404          folderViewController.folder = folderViewController.folder;
405          if (currentFolderIsRoot) {
406              container.count += 1;
407              container.rootFolder = folder;
408              [self.account.containers setObject:container forKey:container.name];
409          }
410          if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
411              [folderViewController setDetailViewController];
412          allowOverwrite = NO;
413          [self dismissModalViewControllerAnimated:YES];
414      }
415      failure:^(OpenStackRequest *request) {
416          [activityIndicatorView removeFromSuperview];
417          allowOverwrite = NO;
418          [self alert:@"There was a problem uploading this file." request:request];            
419      }];
420 }
421
422 #pragma mark - Alertview delegate
423
424 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
425 {
426     if (buttonIndex == 1) {
427         allowOverwrite = YES;
428         [self saveButtonPressed:nil];
429     } 
430 }
431
432 #pragma mark -
433 #pragma mark Memory management
434
435 - (void)dealloc {
436     [image release];
437     [account release];
438     [container release];
439     [folder release];
440     [folderViewController release];
441     [nameTextField release];
442     [formatLabel release];
443     [slider release];
444     [qualityActivityIndicatorView release];
445     [super dealloc];
446 }
447
448
449 @end
450