r84892 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r84891‎ | r84892 | r84893 >
Date:23:15, 27 March 2011
Author:hartman
Status:deferred
Tags:
Comment:
Add support for uploading with GPS and EXIF
Modified paths:
  • /trunk/tools/WikiSnaps/Classes/CommonsUpload.h (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/CommonsUpload.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/Configuration.h (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/ImageDetailsViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/ImageUploadViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/LicensePickerViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/PhotoPickerAppDelegate.h (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/PhotoPickerAppDelegate.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/SettingsViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/SourcePickerViewController.h (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/SourcePickerViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/WikiSnaps.xcodeproj/project.pbxproj (modified) (history)

Diff [purge]

Index: trunk/tools/WikiSnaps/Classes/ImageUploadViewController.m
@@ -33,16 +33,16 @@
3434 - (void)viewDidLoad {
3535 [super viewDidLoad];
3636
37 - uploadOverlayImage.image = [UIImage imageWithData:upload.imageData];
 37+ uploadOverlayImage.image = self.upload.originalImage;
3838 uploadProgressMessage.text = NSLocalizedString( @"uploading", @"Upload progress message" );
3939 uploadProgress.progress = 0.0f;
4040
4141 // view.frame = CGRectMake(0, 20, 320, 460);
42 - // [[UIApplication sharedApplication].keyWindow addSubview:uploadPhotoOverlay];
 42+ // [[UIApplication sharedApplication].keyWindow addSubview:PhotoOverlay];
4343
4444 // Start the actual upload
45 - upload.delegate = self;
46 - [upload uploadImage];
 45+ [self.upload setDelegate: self];
 46+ [self.upload uploadImage];
4747 }
4848
4949 - (void) viewWillAppear:(BOOL)animated{
@@ -77,7 +77,7 @@
7878
7979
8080 - (void)dealloc {
81 - [upload release];
 81+ self.upload = nil;
8282 [super dealloc];
8383 }
8484
@@ -92,8 +92,8 @@
9393 [[UIAlertView alloc] initWithTitle: NSLocalizedString( @"Upload succeeded", @"Title for upload succeeded alert" )
9494 message: nil
9595 delegate: self
96 - cancelButtonTitle: NSLocalizedString( @"Show upload", @"Title for Show upload button in alert view after upload succeeded" )
97 - otherButtonTitles: nil];
 96+ cancelButtonTitle: NSLocalizedString( @"Cancel", @"" )
 97+ otherButtonTitles: NSLocalizedString( @"Show upload", @"Title for Show upload button in alert view after upload succeeded" ),nil];
9898 [alert show];
9999 [alert release];
100100 }
@@ -104,8 +104,12 @@
105105 }
106106
107107 - (void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex {
108 - [[UIApplication sharedApplication] openURL:[NSURL URLWithString: [NSString stringWithFormat: COMMONS_DESTINATION_URL, [upload.title stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];
109 - //[self.navigationController popToRootViewControllerAnimated:YES];
 108+ if( buttonIndex == 0 ) {
 109+ NSLog( @"Cancel" );
 110+ } else if ( buttonIndex == 1 ) {
 111+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString: [NSString stringWithFormat: COMMONS_DESTINATION_URL, [self.upload.imageTitle stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];
 112+ }
 113+ [self.navigationController popToRootViewControllerAnimated:YES];
110114 } // clickedButtonAtIndex
111115
112116
Index: trunk/tools/WikiSnaps/Classes/ImageDetailsViewController.m
@@ -63,9 +63,9 @@
6464
6565
6666 - (void)dealloc {
67 - [titleField release];
68 - [descriptionText release];
69 - [upload release];
 67+ self.titleField = nil;
 68+ self.descriptionText = nil;
 69+ self.upload = nil;
7070 [super dealloc];
7171 }
7272
@@ -76,11 +76,11 @@
7777 -(void)textFieldDidEndEditing:(id)sender {
7878 if(sender == titleField ) {
7979 /* Verify name */
80 - if( ![upload verifyTitle: titleField.text] ) {
81 - titleField.textColor = [UIColor redColor];
 80+ if( ![self.upload verifyTitle: self.titleField.text] ) {
 81+ self.titleField.textColor = [UIColor redColor];
8282 } else {
83 - titleField.textColor = [UIColor blackColor];
84 - [descriptionText becomeFirstResponder];
 83+ self.titleField.textColor = [UIColor blackColor];
 84+ [self.descriptionText becomeFirstResponder];
8585 }
8686 return;
8787 }
@@ -94,12 +94,12 @@
9595 }
9696
9797 - (void)doUpload:(id)sender {
98 - upload.title = [NSString stringWithFormat: @"%@.jpg", titleField.text];
99 - upload.description = descriptionText.text;
100 - [descriptionText resignFirstResponder];
 98+ self.upload.imageTitle = [NSString stringWithFormat: @"%@.jpg", self.titleField.text];
 99+ self.upload.description = self.descriptionText.text;
 100+ [self.descriptionText resignFirstResponder];
101101
102102 ImageUploadViewController *uploadViewController = [[ImageUploadViewController alloc] init];
103 - uploadViewController.upload = upload;
 103+ uploadViewController.upload = self.upload;
104104 [self.navigationController pushViewController:uploadViewController animated:YES];
105105 [uploadViewController release];
106106 }
Index: trunk/tools/WikiSnaps/Classes/SourcePickerViewController.h
@@ -13,13 +13,17 @@
1414 #import "Configuration.h"
1515
1616 @interface SourcePickerViewController : UITableViewController <UINavigationControllerDelegate,
17 - UIImagePickerControllerDelegate> {
 17+ UIImagePickerControllerDelegate>
 18+{
 19+ UIImagePickerController *imagePicker;
1820 BOOL cameraAvailable;
1921 BOOL fakeCameraAvailable;
20 - NSData *imageData;
 22+ UIImage *image;
2123
2224 NSArray *licenses;
2325 }
 26+@property (nonatomic, retain) UIImage *image;
 27+@property (nonatomic, retain) UIImagePickerController *imagePicker;
2428
2529 - (IBAction)settingsPressed:(id)sender;
2630 - (IBAction)infoPressed:(id)sender;
Index: trunk/tools/WikiSnaps/Classes/SourcePickerViewController.m
@@ -9,6 +9,8 @@
1010 // Based on Photopicker (MIT)
1111
1212 #import "PhotoPickerAppDelegate.h"
 13+#import <AssetsLibrary/AssetsLibrary.h>
 14+#import <ImageIO/ImageIO.h>
1315
1416 #import "SourcePickerViewController.h"
1517 #import "SettingsViewController.h"
@@ -24,13 +26,14 @@
2527
2628 /* Private */
2729 @interface SourcePickerViewController ()
28 - @property (nonatomic, retain) NSData *imageData;
2930 - (void)pickPhoto:(UIImagePickerControllerSourceType)sourceType;
 31+ - (void)detailsForImageWithURL:(NSURL *)assetURL;
3032 @end
3133
3234 @implementation SourcePickerViewController
3335
34 -@synthesize imageData;
 36+@synthesize image;
 37+@synthesize imagePicker;
3538
3639 #pragma mark -
3740 #pragma mark Actions
@@ -63,8 +66,8 @@
6467
6568 /* Open a photopicker */
6669 - (void)pickPhoto:(UIImagePickerControllerSourceType)sourceType {
67 - UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
68 - imagePickerController.delegate = self;
 70+ self.imagePicker = [[[UIImagePickerController alloc] init] autorelease];
 71+ self.imagePicker.delegate = self;
6972
7073 // If the camera is force enabled, show the library instead.
7174 #ifdef FORCE_ENABLE_CAMERA
@@ -73,13 +76,68 @@
7477 }
7578 #endif
7679
77 - imagePickerController.sourceType = sourceType;
 80+ self.imagePicker.sourceType = sourceType;
7881 // imagePickerController.allowsImageEditing = YES;
79 -
80 - [self presentModalViewController:imagePickerController animated:YES];
81 - [imagePickerController release];
 82+
 83+ [self presentModalViewController:self.imagePicker animated:YES];
8284 }
8385
 86+- (NSMutableDictionary*)currentLocation {
 87+ NSMutableDictionary *locDict = [[NSMutableDictionary alloc] init];
 88+ PhotoPickerAppDelegate *appDelegate =
 89+ (PhotoPickerAppDelegate *) [UIApplication sharedApplication].delegate;
 90+ CLLocation *lastLocation = appDelegate.lastLocation;
 91+
 92+ if (lastLocation != nil) {
 93+ CLLocationDegrees exifLatitude = lastLocation.coordinate.latitude;
 94+ CLLocationDegrees exifLongitude = lastLocation.coordinate.longitude;
 95+ CLLocationDistance exifAltitude = lastLocation.altitude;
 96+
 97+ [locDict setObject:@"2.2.0.0" forKey:(NSString *)kCGImagePropertyGPSVersion];
 98+ [locDict setObject:lastLocation.timestamp forKey:(NSString*)kCGImagePropertyGPSTimeStamp];
 99+
 100+ if (exifLatitude < 0.0) {
 101+ exifLatitude = exifLatitude*(-1);
 102+ [locDict setObject:@"S" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
 103+ } else {
 104+ [locDict setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
 105+ }
 106+ [locDict setObject:[NSNumber numberWithFloat:exifLatitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];
 107+
 108+ if (exifLongitude < 0.0) {
 109+ exifLongitude=exifLongitude*(-1);
 110+ [locDict setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
 111+ } else {
 112+ [locDict setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
 113+ }
 114+ [locDict setObject:[NSNumber numberWithFloat:exifLongitude] forKey:(NSString*) kCGImagePropertyGPSLongitude];
 115+
 116+ if (!isnan(exifAltitude)){
 117+ if (exifAltitude < 0) {
 118+ exifAltitude = -exifAltitude;
 119+ [locDict setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
 120+ } else {
 121+ [locDict setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
 122+ }
 123+ [locDict setObject:[NSNumber numberWithFloat:exifAltitude] forKey:(NSString *)kCGImagePropertyGPSAltitude];
 124+ }
 125+
 126+ // Speed, must be converted from m/s to km/h
 127+ if (lastLocation.speed >= 0){
 128+ [locDict setObject:@"K" forKey:(NSString *)kCGImagePropertyGPSSpeedRef];
 129+ [locDict setObject:[NSNumber numberWithFloat:lastLocation.speed*3.6] forKey:(NSString *)kCGImagePropertyGPSSpeed];
 130+ }
 131+
 132+ // Heading
 133+ if (lastLocation.course >= 0){
 134+ [locDict setObject:@"T" forKey:(NSString *)kCGImagePropertyGPSTrackRef];
 135+ [locDict setObject:[NSNumber numberWithFloat:lastLocation.course] forKey:(NSString *)kCGImagePropertyGPSTrack];
 136+ }
 137+ }
 138+
 139+ return [locDict autorelease];
 140+}
 141+
84142 #pragma mark -
85143 #pragma mark View lifecycle
86144
@@ -214,18 +272,42 @@
215273 didFinishPickingMediaWithInfo:(NSDictionary *)info {
216274 NSLog(@"Image info: %@",info);
217275
218 - UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];
 276+ self.image = [info valueForKey:UIImagePickerControllerOriginalImage];
 277+ NSURL *imageURL = [info valueForKey:UIImagePickerControllerMediaURL];
 278+ NSMutableDictionary *metaData = [NSMutableDictionary dictionaryWithDictionary:[info valueForKey:UIImagePickerControllerMediaMetadata]];
219279
220 - image = [image correctOrientation:image];
 280+ [metaData setObject:[self currentLocation] forKey: (NSString *)kCGImagePropertyGPSDictionary];
221281
222282 // Store the image on the Camera Roll
223283 if( picker.sourceType == UIImagePickerControllerSourceTypeCamera ) {
224 - UIImageWriteToSavedPhotosAlbum( image, nil, nil, nil );
 284+ Class assestsLibClass = (NSClassFromString(@"ALAssetsLibrary"));
 285+ if( assestsLibClass != nil ) {
 286+ ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
 287+
 288+ ALAssetsLibraryWriteImageCompletionBlock completionBlock = ^(NSURL *newURL, NSError *error) {
 289+ if (error) {
 290+ NSLog( @"Could not write image to photoalbum: %@", error );
 291+ } else {
 292+ [self detailsForImageWithURL: newURL];
 293+ }
 294+ };
 295+
 296+ [library writeImageToSavedPhotosAlbum:[self.image CGImage] metadata:metaData completionBlock:completionBlock];
 297+ return;
 298+ } else {
 299+ self.image = [self.image correctOrientation:self.image];
 300+ UIImageWriteToSavedPhotosAlbum( self.image, nil, nil, nil );
 301+ }
225302 }
 303+ [self detailsForImageWithURL: imageURL];
226304
 305+}
 306+
 307+- (void)detailsForImageWithURL:(NSURL *)assetURL {
227308 // Prepare upload
228309 CommonsUpload *ourUpload = [[CommonsUpload alloc] init];
229 - ourUpload.imageData = UIImageJPEGRepresentation(image, 0.85f);
 310+ ourUpload.originalImage = self.image;
 311+ ourUpload.imageURL = assetURL;
230312
231313 ImageDetailsViewController *detailsController = [[ImageDetailsViewController alloc] init];
232314
@@ -235,8 +317,8 @@
236318 //to push the UIView.
237319 [self.navigationController pushViewController:detailsController animated:YES];
238320 [detailsController release];
239 -
240 - [picker dismissModalViewControllerAnimated:YES];
 321+
 322+ [self dismissModalViewControllerAnimated:YES];
241323 }
242324
243325
@@ -252,7 +334,7 @@
253335 if (shouldCancelApp) {
254336 //[self cancelApp];
255337 } else {
256 - [picker dismissModalViewControllerAnimated:YES];
 338+ [self dismissModalViewControllerAnimated:YES];
257339
258340 //[self showPhotoSourceMenuOrPhotoSourceDirectly];
259341 }
@@ -275,6 +357,8 @@
276358
277359
278360 - (void)dealloc {
 361+ self.image = nil;
 362+ self.imagePicker = nil;
279363 [super dealloc];
280364 }
281365
Index: trunk/tools/WikiSnaps/Classes/SettingsViewController.m
@@ -43,8 +43,8 @@
4444 PhotoPickerAppDelegate *appDelegate =
4545 (PhotoPickerAppDelegate *) [UIApplication sharedApplication].delegate;
4646
47 - licenses = appDelegate.licenses;
48 - selectedLicense = 0;
 47+ self.licenses = appDelegate.licenses;
 48+ self.selectedLicense = 0;
4949
5050 [self loadData];
5151
@@ -97,33 +97,36 @@
9898
9999
100100 - (void)dealloc {
101 - [username release];
102 - [password release];
103 - [license release];
104 - [save release];
 101+ self.username = nil;
 102+ self.password = nil;
 103+ self.license = nil;
 104+ self.save = nil;
 105+
 106+ self.licenses = nil;
 107+
105108 [super dealloc];
106109 }
107110
108111 #pragma mark Data loading and saving
109112
110113 - (void)loadData {
111 - username.text = [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY];
 114+ self.username.text = [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY];
112115
113116 NSString *licenseDefault = [[NSUserDefaults standardUserDefaults] stringForKey: COMMONS_LICENSE_KEY];
114 - NSEnumerator *enumerator = [licenses objectEnumerator];
 117+ NSEnumerator *enumerator = [self.licenses objectEnumerator];
115118 NSDictionary *aLicense;
116119 while( licenseDefault != nil && (aLicense = [enumerator nextObject]) ) {
117120 if( [licenseDefault compare: [aLicense objectForKey:@"short"]] == NSOrderedSame ) {
118 - license.text = [aLicense objectForKey:@"name"];
 121+ self.license.text = [aLicense objectForKey:@"name"];
119122 break;
120123 }
121 - selectedLicense++;
 124+ self.selectedLicense++;
122125 }
123 - if( selectedLicense == [licenses count] )
124 - selectedLicense = 0;
 126+ if( self.selectedLicense == [self.licenses count] )
 127+ self.selectedLicense = 0;
125128
126129 NSError *error = nil;
127 - password.text = [SFHFKeychainUtils getPasswordForUsername:username.text andServiceName:COMMONS_KEYCHAIN_KEY error: &error];
 130+ self.password.text = [SFHFKeychainUtils getPasswordForUsername:username.text andServiceName:COMMONS_KEYCHAIN_KEY error: &error];
128131 if( error ) {
129132 NSLog( @"pasword storage problem: %@", [error localizedDescription] );
130133 }
@@ -132,7 +135,7 @@
133136 - (void)saveData {
134137 NSError *error = nil;
135138 NSString *oldUsername = [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY];
136 - if( [username.text compare:oldUsername] != NSOrderedSame ) {
 139+ if( [self.username.text compare:oldUsername] != NSOrderedSame ) {
137140 /* Delete password for previous username */
138141 [SFHFKeychainUtils deleteItemForUsername:oldUsername andServiceName:COMMONS_KEYCHAIN_KEY error:&error];
139142 if( error ) {
@@ -142,13 +145,13 @@
143146 }
144147
145148 /* Save the data */
146 - [[NSUserDefaults standardUserDefaults] setObject:username.text forKey:COMMONS_USERNAME_KEY];
 149+ [[NSUserDefaults standardUserDefaults] setObject:self.username.text forKey:COMMONS_USERNAME_KEY];
147150
148 - NSDictionary *aLicense = [licenses objectAtIndex:selectedLicense];
 151+ NSDictionary *aLicense = [self.licenses objectAtIndex:self.selectedLicense];
149152 [[NSUserDefaults standardUserDefaults] setObject:[aLicense objectForKey:@"short"] forKey:COMMONS_LICENSE_KEY];
150153
151154 /* Store the password in the keychain */
152 - [SFHFKeychainUtils storeUsername: username.text andPassword: password.text forServiceName:COMMONS_KEYCHAIN_KEY updateExisting: YES error: &error];
 155+ [SFHFKeychainUtils storeUsername: self.username.text andPassword: self.password.text forServiceName:COMMONS_KEYCHAIN_KEY updateExisting: YES error: &error];
153156
154157 if( error ) {
155158 NSLog( @"pasword storage problem: %@", [error localizedDescription] );
@@ -169,7 +172,7 @@
170173
171174 - (IBAction)textFieldDidEnd:(id)sender {
172175 if(sender == username ) {
173 - [password becomeFirstResponder];
 176+ [self.password becomeFirstResponder];
174177 return;
175178 }
176179 [sender resignFirstResponder];
@@ -186,8 +189,8 @@
187190 - (IBAction) pickLicensePicker:(id)sender{
188191 LicensePickerViewController *picker = [[LicensePickerViewController alloc] initWithNibName:@"LicensePickerViewController" bundle:nil];
189192 picker.delegate = self;
190 - picker.licenses = licenses;
191 - picker.selectedLicense = selectedLicense;
 193+ picker.licenses = self.licenses;
 194+ picker.selectedLicense = self.selectedLicense;
192195 [self presentModalViewController:picker animated:YES];
193196 [picker release];
194197 }
@@ -196,8 +199,8 @@
197200
198201 -(void)licensePickerDidFinish:(int)theSelectedLicense {
199202 NSDictionary *licenseDict = [licenses objectAtIndex:theSelectedLicense];
200 - license.text = [licenseDict objectForKey:@"name"];
201 - selectedLicense = theSelectedLicense;
 203+ self.license.text = [licenseDict objectForKey:@"name"];
 204+ self.selectedLicense = theSelectedLicense;
202205 [self dismissModalViewControllerAnimated:YES];
203206 }
204207
Index: trunk/tools/WikiSnaps/Classes/Configuration.h
@@ -13,6 +13,7 @@
1414 #define COMMONS_USERNAME_KEY @"CommonsUsernameKey"
1515 #define COMMONS_LICENSE_KEY @"CommonsLicenseKey"
1616 #define COMMONS_KEYCHAIN_KEY @"CommonsKey"
 17+#define GEOTAGGING_KEY @"GeoTaggingKey"
1718
1819 // Constants
1920 #define COMMONS_DESTINATION_URL @"http://commons.wikimedia.org/wiki/File:%@"
Index: trunk/tools/WikiSnaps/Classes/CommonsUpload.h
@@ -20,8 +20,9 @@
2121
2222
2323 @interface CommonsUpload : NSObject {
24 - NSData *imageData;
25 - NSString *title;
 24+ UIImage *originalImage;
 25+ NSURL *imageURL;
 26+ NSString *imageTitle;
2627 NSString *description;
2728 NSString *token;
2829 NSString *editToken;
@@ -29,8 +30,9 @@
3031 id <CommonsUploadDelegate> delegate;
3132 }
3233
33 -@property (nonatomic, retain) NSData *imageData;
34 -@property (nonatomic, retain) NSString *title;
 34+@property (nonatomic, retain) UIImage *originalImage;
 35+@property (nonatomic, retain) NSURL *imageURL;
 36+@property (nonatomic, retain) NSString *imageTitle;
3537 @property (nonatomic, retain) NSString *description;
3638 @property (nonatomic, assign) id <CommonsUploadDelegate> delegate;
3739
Index: trunk/tools/WikiSnaps/Classes/PhotoPickerAppDelegate.h
@@ -8,6 +8,7 @@
99 // Based on Photopicker (MIT)
1010
1111 #import <UIKit/UIKit.h>
 12+#import <CoreLocation/CoreLocation.h>
1213
1314 #import "Configuration.h"
1415
@@ -15,27 +16,37 @@
1617 @class PhotoPickerViewController;
1718
1819
19 -@interface PhotoPickerAppDelegate : NSObject <UIApplicationDelegate> {
 20+@interface PhotoPickerAppDelegate : NSObject <UIApplicationDelegate,
 21+ CLLocationManagerDelegate> {
2022 int defaultImageSource;
2123 BOOL justInstalled;
 24+
2225 NSString *postContext;
2326 UIWindow *window;
2427 PhotoPickerViewController *viewController;
2528 UINavigationController *navController;
 29+
2630 NSArray *licenses;
 31+
 32+ CLLocationManager *locationManager;
 33+ CLLocation *lastLocation;
2734 }
2835
29 -@property (nonatomic, assign) int defaultImageSource;
 36+@property int defaultImageSource;
3037
3138 // Checking the justInstalled property could be useful if you want to point the somewhere special
3239 // the first time they run the app.
33 -@property (nonatomic, assign) BOOL justInstalled;
 40+@property BOOL justInstalled;
3441
3542 @property (nonatomic, retain) NSString *postContext;
3643 @property (nonatomic, retain) IBOutlet PhotoPickerViewController *viewController;
3744 @property (nonatomic, retain) IBOutlet UINavigationController *navController;
3845 @property (nonatomic, retain) IBOutlet UIWindow *window;
 46+
3947 @property (nonatomic, retain) NSArray *licenses;
4048
 49+@property (nonatomic, assign) CLLocationManager *locationManager;
 50+@property (nonatomic, retain) CLLocation *lastLocation;
 51+
4152 @end
4253
Index: trunk/tools/WikiSnaps/Classes/LicensePickerViewController.m
@@ -34,9 +34,9 @@
3535 dismissButton.action = @selector( dismissLicensePicker: );
3636
3737 [pickerControl selectRow:selectedLicense inComponent:0 animated: NO];
38 - NSDictionary *aLicense = [licenses objectAtIndex:selectedLicense];
39 - pickerLabel.text = [aLicense objectForKey:@"name"];
40 - [descriptionText loadHTMLString: [aLicense objectForKey:@"description"] baseURL: nil];
 38+ NSDictionary *aLicense = [self.licenses objectAtIndex:selectedLicense];
 39+ self.pickerLabel.text = [aLicense objectForKey:@"name"];
 40+ [self.descriptionText loadHTMLString: [aLicense objectForKey:@"description"] baseURL: nil];
4141 }
4242
4343
@@ -63,13 +63,18 @@
6464
6565
6666 - (void)dealloc {
 67+ self.descriptionText = nil;
 68+ self.pickerLabel = nil;
 69+ self.licenses = nil;
 70+ self.delegate = nil;
 71+
6772 [super dealloc];
6873 }
6974
7075 #pragma mark Actions
7176 - (IBAction) dismissLicensePicker: (id) sender
7277 {
73 - [delegate licensePickerDidFinish: [pickerControl selectedRowInComponent:0]];
 78+ [self.delegate licensePickerDidFinish: [pickerControl selectedRowInComponent:0]];
7479 }
7580
7681 #pragma mark UIPickerViewDelegate
@@ -78,7 +83,7 @@
7984 titleForRow: (NSInteger)row
8085 forComponent: (NSInteger)component
8186 {
82 - NSDictionary *dict = [licenses objectAtIndex:row];
 87+ NSDictionary *dict = [self.licenses objectAtIndex:row];
8388 if( dict != nil ) {
8489 return [dict objectForKey:@"short"];
8590 }
@@ -89,10 +94,10 @@
9095 didSelectRow:(NSInteger)row
9196 inComponent:(NSInteger)component
9297 {
93 - NSDictionary *dict = [licenses objectAtIndex:row];
 98+ NSDictionary *dict = [self.licenses objectAtIndex:row];
9499 if( dict != nil ) {
95 - pickerLabel.text = [dict objectForKey:@"name"];
96 - [descriptionText loadHTMLString: [dict objectForKey:@"description"] baseURL: nil];
 100+ self.pickerLabel.text = [dict objectForKey:@"name"];
 101+ [self.descriptionText loadHTMLString: [dict objectForKey:@"description"] baseURL: nil];
97102 }
98103 }
99104
@@ -106,7 +111,7 @@
107112
108113 - (NSInteger)pickerView: (UIPickerView *)aPickerView numberOfRowsInComponent: (NSInteger)component
109114 {
110 - NSInteger numberOfRows = [licenses count];
 115+ NSInteger numberOfRows = [self.licenses count];
111116
112117 return numberOfRows;
113118 }
Index: trunk/tools/WikiSnaps/Classes/CommonsUpload.m
@@ -8,6 +8,8 @@
99 // Dual-licensed MIT and BSD
1010
1111 #import "CommonsUpload.h"
 12+#import <AssetsLibrary/AssetsLibrary.h>
 13+
1214 #import "Configuration.h"
1315 #import "ASIFormDataRequest.h"
1416 #import "XMLReader.h"
@@ -23,8 +25,22 @@
2426
2527 @implementation CommonsUpload
2628
27 -@synthesize imageData, title, description, delegate;
 29+@synthesize originalImage;
 30+@synthesize imageURL;
 31+@synthesize imageTitle;
 32+@synthesize description;
 33+@synthesize delegate;
2834
 35+- (void)dealloc {
 36+ self.originalImage = nil;
 37+ self.imageURL = nil;
 38+ self.imageTitle = nil;
 39+ self.description = nil;
 40+ self.delegate = nil;
 41+
 42+ [super dealloc];
 43+}
 44+
2945 - (NSString *)getUploadText {
3046 return @"";
3147 }
@@ -60,14 +76,15 @@
6177
6278 NSLog(@"%@", dateString);
6379
64 - return [NSString stringWithFormat: @"{{Information\n|Description={{en|1=%@}}\n|Author=[[User:%@]]\n|Source={{own}}\n|Date=%@\n|Permission=\n|other_versions=\n}}\n\n== {{int:license}} ==\n%@\n\n[[Category:%@]]",
65 - description,
66 - [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY],
67 - dateString,
68 - [self getLicenseString],
69 - APPLICATION_CATEGORY,
70 - nil
 80+ NSString *result = [NSString stringWithFormat: @"{{Information\n|Description={{en|1=%@}}\n|Author=[[User:%@]]\n|Source={{own}}\n|Date=%@\n|Permission=\n|other_versions=\n}}\n\n== {{int:license}} ==\n%@\n\n[[Category:%@]]",
 81+ self.description,
 82+ [[NSUserDefaults standardUserDefaults] stringForKey: COMMONS_USERNAME_KEY],
 83+ dateString,
 84+ [self getLicenseString],
 85+ APPLICATION_CATEGORY
7186 ];
 87+ NSLog( @"%@", result );
 88+ return result;
7289 }
7390
7491 - (void)uploadImage {
@@ -196,7 +213,7 @@
197214 [newRequest addPostValue:@"query" forKey:@"action"];
198215 [newRequest addPostValue:@"xml" forKey: @"format"];
199216 [newRequest addPostValue:@"edit" forKey:@"intoken"];
200 - [newRequest addPostValue:title forKey:@"titles"];
 217+ [newRequest addPostValue:self.imageTitle forKey:@"titles"];
201218 [newRequest addPostValue:@"info" forKey:@"prop"];
202219
203220 [newRequest setDelegate:self];
@@ -242,7 +259,7 @@
243260 return;
244261 }
245262
246 - editToken = [query objectForKey:@"edittoken"];
 263+ editToken = [query objectForKey:@"edittoken"];
247264 if( !editToken ) {
248265 [delegate uploadFailed: [NSString stringWithFormat:@"could not find edittoken"]];
249266 return;
@@ -250,18 +267,43 @@
251268
252269 //New request
253270 NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
 271+ NSString *uploadDescription = [self getUploadDescription];
254272 ASIFormDataRequest *newRequest = [ASIFormDataRequest requestWithURL:url];
255273 [newRequest setPostFormat:ASIMultipartFormDataPostFormat];
256274
257275 [newRequest addPostValue:@"upload" forKey:@"action"];
258276 [newRequest addPostValue:@"xml" forKey: @"format"];
259277 [newRequest addPostValue:editToken forKey:@"token"];
260 - [newRequest addPostValue:title forKey:@"filename"];
261 - [newRequest addPostValue:[self getUploadDescription] forKey:@"comment"];
262 - [newRequest addPostValue:[self getUploadDescription] forKey:@"text"];
263 - [newRequest addData:imageData forKey:@"file"];
 278+ [newRequest addPostValue:self.imageTitle forKey:@"filename"];
 279+ [newRequest addPostValue:uploadDescription forKey:@"comment"];
 280+ [newRequest addPostValue:uploadDescription forKey:@"text"];
 281+ if( self.imageURL == nil && self.originalImage != nil ) {
 282+ [newRequest addData:UIImageJPEGRepresentation(self.originalImage, 0.85f) forKey:@"file"];
 283+ } else if ( self.imageURL ) {
 284+ ALAssetsLibrary *assetLib = [[[ALAssetsLibrary alloc] init] autorelease];
 285+ ALAssetsLibraryAssetForURLResultBlock resultBlock =
 286+ ^(ALAsset *asset) {
 287+ ALAssetRepresentation *representation = [asset defaultRepresentation];
 288+ Byte *buf = malloc([representation size]);
 289+ NSError *err = nil;
 290+ NSUInteger bytes = [representation getBytes:buf fromOffset:0LL length:[representation size] error:&err];
 291+ if (err || bytes == 0) {
 292+ NSLog( @"Could not read asset: %@", err );
 293+ } else {
 294+ NSData *cumbersomeWayToGetNSData = [NSData dataWithBytesNoCopy:buf length:[representation size] freeWhenDone:YES];
 295+ [newRequest addData:cumbersomeWayToGetNSData forKey:@"file"];
 296+ }
 297+ };
 298+
 299+ [assetLib assetForURL:self.imageURL resultBlock:resultBlock failureBlock:^(NSError *error) {
 300+ NSLog( @"Error finding asset: %@", error);
 301+ }];
 302+ /*
 303+ NSData *tempData = [NSData dataWithContentsOfURL:self.imageURL];
 304+ [newRequest addData:tempData forKey:@"file"];
 305+ */
 306+ }
264307
265 -
266308 [newRequest setDelegate:self];
267309 [newRequest setDidFinishSelector:@selector(requestUploadFinished:)];
268310 [newRequest setDidFailSelector:@selector(requestUploadFailed:)];
Index: trunk/tools/WikiSnaps/Classes/PhotoPickerAppDelegate.m
@@ -12,6 +12,8 @@
1313
1414 @interface PhotoPickerAppDelegate ()
1515 - (void)checkIfJustInstalled;
 16+ - (void)startLocationUpdates;
 17+ - (void)stopLocationUpdates;
1618 @end
1719
1820
@@ -22,10 +24,11 @@
2325 @synthesize postContext;
2426 @synthesize viewController;
2527 @synthesize navController;
26 -@synthesize window;
2728 @synthesize licenses;
2829
 30+@synthesize locationManager, lastLocation;
2931
 32+
3033 - (BOOL)application:(UIApplication *)application
3134 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
3235
@@ -34,28 +37,100 @@
3538 self.defaultImageSource = -1;
3639
3740 NSString *path = [[NSBundle mainBundle] pathForResource:@"Licenses" ofType:@"plist"];
38 - licenses = [[NSMutableArray alloc] initWithContentsOfFile:path];
39 - if( licenses == nil ) {
 41+ self.licenses = [[NSMutableArray alloc] initWithContentsOfFile:path];
 42+ if( self.licenses == nil ) {
4043 NSLog( @"Could not load the licenses information" );
4144 }
4245
4346 [window addSubview:navController.view];
4447 [window makeKeyAndVisible];
45 -
 48+
 49+ [self startLocationUpdates];
4650 return YES;
4751 }
4852
 53+- (void)applicationWillTerminate:(UIApplication *)application {
 54+ [self stopLocationUpdates];
 55+}
4956
 57+- (void)applicationWillEnterForeground:(UIApplication *)application {
 58+ [self startLocationUpdates];
 59+}
 60+
 61+
 62+- (void)applicationDidEnterBackground:(UIApplication *)application {
 63+ [self stopLocationUpdates];
 64+}
 65+
 66+
5067 - (void)dealloc {
5168 self.viewController = nil;
5269 self.navController = nil;
5370 self.window = nil;
54 - [licenses release];
 71+ self.licenses = nil;
 72+ self.lastLocation = nil;
5573
5674 [super dealloc];
5775 }
5876
 77+#pragma mark Location services
 78+- (void)startLocationUpdates {
 79+ // Create the location manager if this object does not
 80+ // already have one.
 81+ if( [CLLocationManager locationServicesEnabled] &&
 82+ //[[NSUserDefaults standardUserDefaults] boolForKey: GEOTAGGING_KEY] )
 83+ TRUE )
 84+ {
 85+ if (nil == self.locationManager)
 86+ self.locationManager = [[CLLocationManager alloc] init];
 87+
 88+ self.locationManager.delegate = self;
 89+ self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
 90+
 91+ // Set a movement threshold for new events.
 92+ self.locationManager.distanceFilter = 500;
 93+
 94+ [self.locationManager startUpdatingLocation];
 95+
 96+ if( [self.locationManager headingAvailable] ) {
 97+ [self.locationManager startUpdatingHeading];
 98+ }
 99+ }
 100+}
59101
 102+- (void)stopLocationUpdates {
 103+ if( self.locationManager != nil ) {
 104+ [self.locationManager stopUpdatingHeading];
 105+ [self.locationManager stopUpdatingLocation];
 106+ [self.locationManager release];
 107+ self.locationManager = nil;
 108+ }
 109+}
 110+
 111+#pragma mark LocationManager Delegate
 112+
 113+// Delegate method from the CLLocationManagerDelegate protocol.
 114+- (void)locationManager:(CLLocationManager *)manager
 115+ didUpdateToLocation:(CLLocation *)newLocation
 116+ fromLocation:(CLLocation *)oldLocation
 117+{
 118+ // If it's a relatively recent event, turn off updates to save power
 119+ NSDate* eventDate = newLocation.timestamp;
 120+ NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
 121+ if (abs(howRecent) < 15.0)
 122+ {
 123+ NSLog(@"latitude %+.6f, longitude %+.6f\n",
 124+ newLocation.coordinate.latitude,
 125+ newLocation.coordinate.longitude);
 126+ }
 127+ self.lastLocation = newLocation;
 128+}
 129+
 130+- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
 131+{
 132+ NSLog(@"%@", error );
 133+}
 134+
60135 #pragma mark Private
61136
62137
@@ -66,6 +141,7 @@
67142 if (!testForInstallKey) {
68143 self.justInstalled = YES;
69144 [defaults setBool:YES forKey:@"installed"];
 145+ [defaults setBool:NO forKey:GEOTAGGING_KEY];
70146 }
71147 }
72148
Index: trunk/tools/WikiSnaps/WikiSnaps.xcodeproj/project.pbxproj
@@ -25,6 +25,9 @@
2626 4971EC87133E77FD0043DD67 /* Licenses.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4971EC86133E77FD0043DD67 /* Licenses.plist */; };
2727 4971ED31133E9DD90043DD67 /* LicensePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4971ED2F133E9DD90043DD67 /* LicensePickerViewController.m */; };
2828 4971ED32133E9DD90043DD67 /* LicensePickerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4971ED30133E9DD90043DD67 /* LicensePickerViewController.xib */; };
 29+ 49E03406133F6C1300BFE247 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49E03405133F6C1300BFE247 /* CoreLocation.framework */; };
 30+ 49E0340A133F72A100BFE247 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49E03409133F72A100BFE247 /* AssetsLibrary.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
 31+ 49E03463133F814D00BFE247 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49E03462133F814D00BFE247 /* ImageIO.framework */; };
2932 A127DA4012E0DF9700149FC9 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A127DA3E12E0DF9700149FC9 /* SettingsViewController.m */; };
3033 A127DA4112E0DF9700149FC9 /* SettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A127DA3F12E0DF9700149FC9 /* SettingsViewController.xib */; };
3134 A1338B8612E0F01700662B28 /* ASIAuthenticationDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = A1338B7312E0F01700662B28 /* ASIAuthenticationDialog.m */; };
@@ -80,6 +83,9 @@
8184 4971ED2E133E9DD90043DD67 /* LicensePickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LicensePickerViewController.h; sourceTree = "<group>"; };
8285 4971ED2F133E9DD90043DD67 /* LicensePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LicensePickerViewController.m; sourceTree = "<group>"; };
8386 4971ED30133E9DD90043DD67 /* LicensePickerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = LicensePickerViewController.xib; path = Classes/LicensePickerViewController.xib; sourceTree = "<group>"; };
 87+ 49E03405133F6C1300BFE247 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
 88+ 49E03409133F72A100BFE247 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; };
 89+ 49E03462133F814D00BFE247 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
8490 8D1107310486CEB800E47090 /* WikiSnaps-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "WikiSnaps-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
8591 A127DA3D12E0DF9700149FC9 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = "<group>"; };
8692 A127DA3E12E0DF9700149FC9 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = "<group>"; };
@@ -142,6 +148,9 @@
143149 A159144712E0F69C0003BBE2 /* MobileCoreServices.framework in Frameworks */,
144150 A159144B12E0F6BE0003BBE2 /* SystemConfiguration.framework in Frameworks */,
145151 49354963130096A000A7A44E /* Security.framework in Frameworks */,
 152+ 49E03406133F6C1300BFE247 /* CoreLocation.framework in Frameworks */,
 153+ 49E0340A133F72A100BFE247 /* AssetsLibrary.framework in Frameworks */,
 154+ 49E03463133F814D00BFE247 /* ImageIO.framework in Frameworks */,
146155 );
147156 runOnlyForDeploymentPostprocessing = 0;
148157 };
@@ -231,6 +240,9 @@
232241 A159144612E0F69C0003BBE2 /* MobileCoreServices.framework */,
233242 A159144A12E0F6BE0003BBE2 /* SystemConfiguration.framework */,
234243 49354962130096A000A7A44E /* Security.framework */,
 244+ 49E03405133F6C1300BFE247 /* CoreLocation.framework */,
 245+ 49E03409133F72A100BFE247 /* AssetsLibrary.framework */,
 246+ 49E03462133F814D00BFE247 /* ImageIO.framework */,
235247 );
236248 name = Frameworks;
237249 sourceTree = "<group>";