2 // AddPhotoViewController.m
5 // Created by Mike Mayo on 1/5/11.
6 // Copyright 2011 __MyCompanyName__. All rights reserved.
9 #import "AddPhotoViewController.h"
10 #import "UIViewController+Conveniences.h"
11 #import "UIColor+MoreColors.h"
13 #import "ActivityIndicatorView.h"
14 #import "StorageObject.h"
15 #import "OpenStackAccount.h"
16 #import "AccountManager.h"
19 #import "FolderViewController.h"
20 #import "NSObject+Conveniences.h"
21 #import "APICallback.h"
22 #import "ComputeModel.h"
26 // give me the option to add a description of the file and the URL - date/time stamped
28 @implementation AddPhotoViewController
30 @synthesize image, account, container, folder, folderViewController, isFromCamera;
32 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
33 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
37 #pragma mark View lifecycle
41 self.navigationItem.title = @"Add Photo";
45 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
46 nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(98.0, 13.0, 400.0, 24.0)];
48 nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(79.0, 13.0, 222.0, 24.0)];
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]];
60 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
61 formatLabel = [[UILabel alloc] initWithFrame:CGRectMake(41.0, 15.5, 458.0, 18.0)];
63 formatLabel = [[UILabel alloc] initWithFrame:CGRectMake(21.0, 15.5, 280.0, 18.0)];
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;
71 slider = [[UISlider alloc] init];
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 (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
77 nameTextField.frame = CGRectMake(98.0, 13.0, 400.0 - size.width, 24.0);
79 nameTextField.frame = CGRectMake(79.0, 13.0, 222.0 - size.width, 24.0);
84 - (void)viewWillAppear:(BOOL)animated {
85 [super viewWillAppear:animated];
86 // data = UIImageJPEGRepresentation(image, slider.value);
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
101 - (void)viewDidAppear:(BOOL)animated {
102 [super viewDidAppear:animated];
103 [qualityActivityIndicatorView release];
104 qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Calculating size..."] text:@"Calculating size..."];
105 [qualityActivityIndicatorView addToView:self.view];
106 [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO];
110 #pragma mark Table view data source
112 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
113 if ([format isEqualToString:@".jpg"]) {
115 return isFromCamera ? 1 : 2;
117 return isFromCamera ? 2 : 3;
120 return transitioning ? 3 : 2;
124 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
125 if (section == kName) {
127 } else if (section == formatSection) {
129 } else if (section == qualitySection) {
136 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
137 if (indexPath.section == qualitySection) {
138 return tableView.rowHeight + slider.frame.size.height + 3.0;
140 return tableView.rowHeight;
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.";
153 - (UITableViewCell *)tableView:(UITableView *)tableView nameCellForRowAtIndexPath:(NSIndexPath *)indexPath {
154 static NSString *CellIdentifier = @"NameCell";
155 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
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];
166 - (UITableViewCell *)tableView:(UITableView *)tableView qualityCellForRowAtIndexPath:(NSIndexPath *)indexPath {
167 static NSString *CellIdentifier = @"QualityCell";
169 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
171 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
172 cell.selectionStyle = UITableViewCellSelectionStyleNone;
174 UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 13.0, 280.0, 20.0)];
175 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
176 textLabel.frame = CGRectMake(41.0, 13.0, 458.0, 20.0);
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];
185 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
186 slider.frame = CGRectMake(41.0, 38.0, 458.0, slider.frame.size.height);
188 slider.frame = CGRectMake(20.0, 38.0, 280.0, slider.frame.size.height);
191 [slider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventValueChanged];
192 [slider addTarget:self action:@selector(sliderFinished:) forControlEvents:UIControlEventTouchUpInside];
194 [cell addSubview:slider];
195 qualityLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 14.0, 280.0, 18.0)];
196 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
197 qualityLabel.frame = CGRectMake(41.0, 14.0, 458.0, 18.0);
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];
206 qualityLabel.text = [NSString stringWithFormat:@"%.0f%%", slider.value * 100];
211 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
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];
218 static NSString *CellIdentifier = @"Cell";
220 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
222 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
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;
238 cell.accessoryType = UITableViewCellAccessoryNone;
240 } else if (indexPath.row == 1) {
241 cell.textLabel.text = @"PNG";
242 cell.detailTextLabel.text = @"";
243 if ([format isEqualToString:@".png"]) {
244 cell.accessoryType = UITableViewCellAccessoryCheckmark;
246 cell.accessoryType = UITableViewCellAccessoryNone;
250 cell.textLabel.text = @"Quality";
251 cell.detailTextLabel.text = @"65%";
252 cell.selectionStyle = UITableViewCellSelectionStyleNone;
260 #pragma mark Table view delegate
262 - (void)updateFormat {
266 NSArray *indexPaths = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:formatSection], [NSIndexPath indexPathForRow:1 inSection:formatSection], nil];
267 [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
271 if ([format isEqualToString:@".jpg"]) {
272 [self.tableView insertSections:[NSIndexSet indexSetWithIndex:qualitySection] withRowAnimation:UITableViewRowAnimationBottom];
274 [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:qualitySection] withRowAnimation:UITableViewRowAnimationTop];
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] initWithFrame:[ActivityIndicatorView frameForText:@"Calculating size..."] text:@"Calculating size..."];
283 [qualityActivityIndicatorView addToView:self.view];
284 [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO];
288 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
289 if (indexPath.section == formatSection) {
290 if (indexPath.row == 0) {
292 } else { //if (indexPath.row == 1) {
295 // [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
296 [NSTimer scheduledTimerWithTimeInterval:0.35 target:self selector:@selector(updateFormat) userInfo:nil repeats:NO];
301 #pragma mark Text Field Delegate
303 - (BOOL)textFieldShouldReturn:(UITextField *)textField {
304 [textField resignFirstResponder];
311 - (void)calculateSize {
312 if ([format isEqualToString:@".jpg"]) {
314 data = UIImageJPEGRepresentation(image, slider.value);
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];
325 data = UIImagePNGRepresentation(image);
330 [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:kName]] withRowAnimation:UITableViewRowAnimationNone];
331 [qualityActivityIndicatorView removeFromSuperview];
334 - (void)sliderFinished:(id)sender {
335 [qualityActivityIndicatorView release];
336 qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Calculating size..."] text:@"Calculating size..."];
337 [qualityActivityIndicatorView addToView:self.view];
338 [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO];
341 - (void)sliderMoved:(id)sender {
342 qualityLabel.text = [NSString stringWithFormat:@"%.0f%%", slider.value * 100];
346 #pragma mark Save Button
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];
354 objectName = [NSString stringWithFormat:@"%@%@", nameTextField.placeholder, format];
357 BOOL objectNameHasTrailingSlash = [objectName hasSuffix:@"/"];
358 NSUInteger indexOfFirstSlash = [objectName rangeOfString:@"/"].location;
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"];
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
372 cancelButtonTitle:@"Cancel"
373 otherButtonTitles:@"OK", nil];
378 [activityIndicatorView release];
379 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Uploading..." withProgress:YES] text:@"Uploading..." withProgress:YES];
380 [activityIndicatorView addToView:self.view];
382 StorageObject *object = [[StorageObject alloc] init];
383 object.name = objectName;
384 object.fullPath = [NSString stringWithFormat:@"%@/%@", [folder fullPath], object.name];
385 object.fullPath = [object.fullPath substringFromIndex:1];
387 if ([format isEqualToString:@".jpg"]) {
388 object.contentType = @"image/jpeg";
390 object.contentType = @"image/png";
394 object.bytes = [data length];
396 [[self.account.manager writeObject:self.container object:object downloadProgressDelegate:activityIndicatorView.progressView]
397 success:^(OpenStackRequest *request) {
398 [activityIndicatorView removeFromSuperview];
400 object.sharing = folder.sharing;
401 object.lastModified = [ComputeModel dateFromRFC1123String:[request.responseHeaders objectForKey:@"Date"]];
402 BOOL currentFolderIsRoot = NO;
403 if ([folderViewController.folder isEqual:container.rootFolder]) {
404 currentFolderIsRoot = YES;
406 [folder.objects setObject:object forKey:object.name];
407 [folderViewController.tableView reloadData];
408 if (currentFolderIsRoot) {
409 container.count += 1;
410 container.rootFolder = folder;
411 [self.account.containers setObject:container forKey:container.name];
413 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
414 [folderViewController setDetailViewController];
416 [self dismissModalViewControllerAnimated:YES];
418 failure:^(OpenStackRequest *request) {
419 [activityIndicatorView removeFromSuperview];
421 [self alert:@"There was a problem uploading this file." request:request];
425 #pragma mark - Alertview delegate
427 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
429 if (buttonIndex == 1) {
430 allowOverwrite = YES;
431 [self saveButtonPressed:nil];
436 #pragma mark Memory management
443 [folderViewController release];
444 [nameTextField release];
445 [formatLabel release];
447 [qualityActivityIndicatorView release];
448 [activityIndicatorView release];