2 // DecoderViewController.m
5 // Created by Christian Brunschen on 22/05/2008.
7 * Copyright 2008 ZXing authors
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
22 #import "DecoderViewController.h"
24 #import "NSString+HTML.h"
25 #import "ResultParser.h"
26 #import "ParsedResult.h"
27 #import "ResultAction.h"
30 #import "ArchiveController.h"
32 #import "TwoDDecoderResult.h"
35 @implementation DecoderViewController
37 @synthesize cameraBarItem;
38 @synthesize libraryBarItem;
39 @synthesize savedPhotosBarItem;
40 @synthesize archiveBarItem;
41 @synthesize actionBarItem;
43 @synthesize messageView;
44 @synthesize resultView;
45 @synthesize imageView;
52 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
53 if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
54 // Initialization code
55 self.title = @"ZXing";
57 Decoder *d = [[Decoder alloc] init];
66 // Implement loadView if you want to create a view hierarchy programmatically
70 CGRect mViewFrame = self.resultView.bounds;
71 UITextView *mView = [[UITextView alloc] initWithFrame:mViewFrame];
72 mView.backgroundColor = [UIColor yellowColor];
74 mView.editable = false;
75 mView.scrollEnabled = true;
76 mView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
77 UIViewAutoresizingFlexibleWidth |
78 UIViewAutoresizingFlexibleLeftMargin |
79 UIViewAutoresizingFlexibleRightMargin |
80 UIViewAutoresizingFlexibleTopMargin |
81 UIViewAutoresizingFlexibleBottomMargin;
82 self.messageView = mView;
85 [self.resultView addSubview:self.messageView];
87 [self showMessage:NSLocalizedString(@"Please take or choose a picture containing a barcode", @"")];
90 - (void) updateToolbar {
91 self.cameraBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
92 self.savedPhotosBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum];
93 self.libraryBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary];
94 self.archiveBarItem.enabled = true;
95 self.actionBarItem.enabled = (self.result != nil) && ([self.result actions] != nil) && ([self.result actions].count > 0);
99 // If you need to do additional setup after loading the view, override viewDidLoad.
100 - (void)viewDidLoad {
105 - (void)clearImageView {
106 imageView.image = nil;
107 NSArray *subviews = [imageView.subviews copy];
108 for (UIView *view in subviews) {
109 [view removeFromSuperview];
114 - (void)pickAndDecodeFromSource:(UIImagePickerControllerSourceType) sourceType {
116 [self clearImageView];
117 // Create the Image Picker
118 if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
119 UIImagePickerController* picker = [[UIImagePickerController alloc] init];
120 picker.sourceType = sourceType;
121 picker.delegate = self;
122 picker.allowsImageEditing = [[NSUserDefaults standardUserDefaults]
123 boolForKey:@"allowEditing"];
125 // Picker is displayed asynchronously.
126 [self presentModalViewController:picker animated:YES];
128 NSLog(@"Attempted to pick an image with illegal source type '%d'", sourceType);
132 - (IBAction)pickAndDecode:(id) sender {
133 UIImagePickerControllerSourceType sourceType;
134 int i = [sender tag];
137 case 0: sourceType = UIImagePickerControllerSourceTypeCamera; break;
138 case 1: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; break;
139 case 2: sourceType = UIImagePickerControllerSourceTypePhotoLibrary; break;
140 default: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
142 [self pickAndDecodeFromSource:sourceType];
146 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
147 // Return YES for supported orientations
148 return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
152 - (void)didReceiveMemoryWarning {
153 [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
154 // Release anything that's not essential, such as cached data
160 [self clearImageView];
162 [actionBarItem release];
163 [cameraBarItem release];
164 [libraryBarItem release];
165 [savedPhotosBarItem release];
166 [archiveBarItem release];
172 - (void)showMessage:(NSString *)message {
174 NSLog(@"Showing message '%@'", message);
176 self.messageView.text = message;
177 [self.messageView sizeToFit];
180 // DecoderDelegate methods
182 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image {
183 [self clearImageView];
184 [self.imageView setImage:image];
185 [self showMessage:[NSString stringWithFormat:NSLocalizedString(@"Decoding image (%.0fx%.0f) ...", @"shown while image is decoding"), image.size.width, image.size.height]];
188 - (void)decoder:(Decoder *)decoder
189 decodingImage:(UIImage *)image
190 usingSubset:(UIImage *)subset
191 progress:(NSString *)message {
192 [self clearImageView];
193 [self.imageView setImage:subset];
194 [self showMessage:message];
197 - (void)presentResultForString:(NSString *)resultString {
198 self.result = [ResultParser parsedResultForString:resultString];
199 [self showMessage:[self.result stringForDisplay]];
200 self.actions = self.result.actions;
202 NSLog(@"result has %d actions", actions ? 0 : actions.count);
204 [self updateToolbar];
207 - (void)presentResultPoints:(NSArray *)resultPoints
208 forImage:(UIImage *)image
209 usingSubset:(UIImage *)subset {
210 // CGSize imageSize = image.size;
211 CGSize subsetSize = subset.size;
212 CGRect viewBounds = imageView.bounds;
214 float scale = fmin(viewBounds.size.width / subsetSize.width,
215 viewBounds.size.height / subsetSize.height);
216 float xOffset = (viewBounds.size.width - scale * subsetSize.width) / 2.0;
217 float yOffset = (viewBounds.size.height - scale * subsetSize.height) / 2.0;
219 NSLog(@"(%f, %f) image in view with bounds (%f, %f) x (%f, %f)",
220 subsetSize.width, subsetSize.height,
221 viewBounds.origin.x, viewBounds.origin.y,
222 viewBounds.size.width, viewBounds.size.height);
223 NSLog(@"xOffset = %f, yOffset = %f, scale = %f", xOffset, yOffset, scale);
225 for (NSValue *pointValue in resultPoints) {
226 CGPoint point = [pointValue CGPointValue];
227 float x = xOffset + scale * point.x;
228 float y = yOffset + scale * point.y;
229 NSLog(@"have result point @ (%f, %f), imageView point (%f, %f)", point.x, point.y, x, y);
230 CGRect frame = CGRectMake(x - 3, y - 3, 7, 7);
231 UIView *pointView = [[UIView alloc] initWithFrame:frame];
232 pointView.opaque = YES;
233 pointView.backgroundColor = [UIColor greenColor];
234 pointView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
235 [imageView addSubview:pointView];
240 - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
241 [self presentResultForString:twoDResult.text];
243 [self presentResultPoints:twoDResult.points forImage:image usingSubset:subset];
245 // save the scan to the shared database
246 [[Database sharedDatabase] addScanWithText:twoDResult.text];
248 [self performResultAction:self];
251 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
252 [self showMessage:reason];
253 [self updateToolbar];
257 // UIImagePickerControllerDelegate methods
259 - (void)imagePickerController:(UIImagePickerController *)picker
260 didFinishPickingImage:(UIImage *)image
261 editingInfo:(NSDictionary *)editingInfo
263 UIImage *imageToDecode = image;
265 NSLog(@"picked image size = (%f, %f)", image.size.width, image.size.height);
268 UIImage *originalImage = [editingInfo objectForKey:UIImagePickerControllerOriginalImage];
271 NSLog(@"original image size = (%f, %f)", originalImage.size.width, originalImage.size.height);
273 NSValue *cropRectValue = [editingInfo objectForKey:UIImagePickerControllerCropRect];
275 CGRect cropRect = [cropRectValue CGRectValue];
277 NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
279 UIGraphicsBeginImageContext(cropRect.size);
281 [originalImage drawAtPoint:CGPointMake(-CGRectGetMinX(cropRect),
282 -CGRectGetMinY(cropRect))];
284 imageToDecode = UIGraphicsGetImageFromCurrentImageContext();
285 UIGraphicsEndImageContext();
290 [[picker parentViewController] dismissModalViewControllerAnimated:YES];
291 [imageToDecode retain];
293 [self.decoder decodeImage:imageToDecode];
294 [imageToDecode release];
295 [self updateToolbar];
298 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
300 [picker dismissModalViewControllerAnimated:YES];
302 [self updateToolbar];
305 - (void)navigationController:(UINavigationController *)navigationController
306 didShowViewController:(UIViewController *)viewController
307 animated:(BOOL)animated {
311 - (void)navigationController:(UINavigationController *)navigationController
312 willShowViewController:(UIViewController *)viewController
313 animated:(BOOL)animated {
317 - (void)performAction:(ResultAction *)action {
318 [action performActionWithController:self shouldConfirm:NO];
321 - (void)confirmAndPerformAction:(ResultAction *)action {
322 [action performActionWithController:self shouldConfirm:YES];
326 - (IBAction)performResultAction:(id)sender {
327 if (self.result == nil) {
328 NSLog(@"no result to perform an action on!");
332 if (self.actions == nil || self.actions.count == 0) {
333 NSLog(@"result has no actions to perform!");
337 if (self.actions.count == 1) {
338 ResultAction *action = [self.actions lastObject];
340 NSLog(@"Result has the single action, (%@) '%@', performing it",
341 NSStringFromClass([action class]), [action title]);
343 [self performSelector:@selector(confirmAndPerformAction:)
348 NSLog(@"Result has multiple actions, popping up an action sheet");
350 UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithFrame:self.view.bounds];
352 for (ResultAction *action in self.actions) {
353 [actionSheet addButtonWithTitle:[action title]];
356 int cancelIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
357 actionSheet.cancelButtonIndex = cancelIndex;
359 actionSheet.delegate = self;
361 [actionSheet showFromToolbar:self.toolbar];
365 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
366 if (buttonIndex < self.actions.count) {
367 int actionIndex = buttonIndex;
368 ResultAction *action = [self.actions objectAtIndex:actionIndex];
369 [self performSelector:@selector(performAction:)
375 - (IBAction)showArchive:(id)sender {
376 ArchiveController *archiveController = [[ArchiveController alloc] initWithDecoderViewController:self];
377 [[self navigationController] pushViewController:archiveController animated:true];
378 [archiveController release];
381 - (void)showScan:(Scan *)scan {
382 [self clearImageView];
383 [self presentResultForString:scan.text];
384 [[self navigationController] popToViewController:self animated:YES];