Statistics
| Branch: | Tag: | Revision:

root / Classes / AddPhotoViewController.m @ 29cc4957

History | View | Annotate | Download (18 kB)

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 (UI_USER_INTERFACE_IDIOM() == 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 (UI_USER_INTERFACE_IDIOM() == 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 (UI_USER_INTERFACE_IDIOM() == 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 (UI_USER_INTERFACE_IDIOM() == 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] 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];
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 (UI_USER_INTERFACE_IDIOM() == 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 (UI_USER_INTERFACE_IDIOM() == 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 (UI_USER_INTERFACE_IDIOM() == 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] 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];
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] 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];
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
    [activityIndicatorView release];
379
    activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Uploading..." withProgress:YES] text:@"Uploading..." withProgress:YES];
380
    [activityIndicatorView addToView:self.view];
381

    
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];
386
    
387
    if ([format isEqualToString:@".jpg"]) {
388
        object.contentType = @"image/jpeg";
389
    } else {
390
        object.contentType = @"image/png";
391
    }
392

    
393
    object.data = data;
394
    object.bytes = [data length];
395
    
396
    [[self.account.manager writeObject:self.container object:object downloadProgressDelegate:activityIndicatorView.progressView]
397
     success:^(OpenStackRequest *request) {
398
         [activityIndicatorView removeFromSuperview];
399
         object.data = nil;
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;
405
         }
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];
412
         }
413
         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
414
             [folderViewController setDetailViewController];
415
         allowOverwrite = NO;
416
         [self dismissModalViewControllerAnimated:YES];
417
     }
418
     failure:^(OpenStackRequest *request) {
419
         [activityIndicatorView removeFromSuperview];
420
         allowOverwrite = NO;
421
         [self alert:@"There was a problem uploading this file." request:request];            
422
     }];
423
}
424

    
425
#pragma mark - Alertview delegate
426

    
427
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
428
{
429
    if (buttonIndex == 1) {
430
        allowOverwrite = YES;
431
        [self saveButtonPressed:nil];
432
    } 
433
}
434

    
435
#pragma mark -
436
#pragma mark Memory management
437

    
438
- (void)dealloc {
439
    [image release];
440
    [account release];
441
    [container release];
442
    [folder release];
443
    [folderViewController release];
444
    [nameTextField release];
445
    [formatLabel release];
446
    [slider release];
447
    [qualityActivityIndicatorView release];
448
    [activityIndicatorView release];
449
    [super dealloc];
450
}
451

    
452

    
453
@end
454