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"
31 #import "HintsViewController.h"
33 #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 messageTextView;
45 @synthesize messageHelpButton;
46 @synthesize imageView;
53 @synthesize resultPointViews;
55 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
56 if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
57 // Initialization code
58 self.title = NSLocalizedString(@"DecoderViewController AppTitle", @"Barcode Scanner ");
60 Decoder *d = [[Decoder alloc] init];
64 resultPointViews = [[NSMutableArray alloc] init];
69 - (void) hintsReady:(id)sender {
70 HintsViewController *hintsController = sender;
71 [[self navigationController] pushViewController:hintsController animated:true];
72 [hintsController release];
75 - (void) hintsFailed:(id)sender {
76 HintsViewController *hintsController = sender;
77 NSLog(@"Failed to load hints!");
78 [hintsController release];
81 - (void) showHints:(id)sender {
82 NSLog(@"Showing Hints!");
84 HintsViewController *hintsController = [[HintsViewController alloc] initWithTarget:self onSuccess:@selector(hintsReady:) onFailure:@selector(hintsFailed:)];
85 hintsController.title = NSLocalizedString(@"DecoderViewController HintsViewController title", @"Hints");
90 #define HELP_BUTTON_WIDTH (44.0)
91 #define HELP_BUTTON_HEIGHT (55.0)
94 #define FONT_NAME @"TimesNewRomanPSMT"
95 #define FONT_SIZE 16.0
99 [self clearImageView];
100 [self updateToolbar];
101 [self showMessage:NSLocalizedString(@"DecoderViewController take or choose picture", @"Please take or choose a picture containing a barcode") helpButton:YES];
104 // Implement loadView if you want to create a view hierarchy programmatically
108 CGRect messageViewFrame = imageView.frame;
109 UIView *mView = [[UIView alloc] initWithFrame:messageViewFrame];
110 mView.backgroundColor = [UIColor darkGrayColor];
112 mView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
113 UIViewAutoresizingFlexibleWidth |
114 UIViewAutoresizingFlexibleTopMargin;
116 UITextView *mTextView = [[UITextView alloc] initWithFrame:messageViewFrame];
117 mTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
118 UIViewAutoresizingFlexibleWidth;
119 mTextView.editable = false;
120 mTextView.scrollEnabled = true;
121 mTextView.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE];
122 mTextView.textColor = [UIColor whiteColor];
123 mTextView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.0];
124 mTextView.textAlignment = UITextAlignmentLeft;
125 mTextView.alpha = 1.0;
126 [mView addSubview:mTextView];
128 UIButton *mHelpButton = [[UIButton buttonWithType:UIButtonTypeInfoLight] retain];
129 mHelpButton.frame = CGRectMake(messageViewFrame.size.width - HELP_BUTTON_WIDTH, 0.0, HELP_BUTTON_WIDTH, HELP_BUTTON_HEIGHT);
131 mHelpButton.backgroundColor = [UIColor clearColor];
132 [mHelpButton setUserInteractionEnabled:YES];
133 [mHelpButton addTarget:self action:@selector(showHints:) forControlEvents:UIControlEventTouchUpInside];
135 self.messageHelpButton = mHelpButton;
136 [mHelpButton release];
138 self.messageTextView = mTextView;
141 self.messageView = mView;
144 [self.view addSubview:self.messageView];
149 - (void) updateToolbar {
150 self.cameraBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
151 self.savedPhotosBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum];
152 self.libraryBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary];
153 self.archiveBarItem.enabled = true;
154 self.actionBarItem.enabled = (self.result != nil) && ([self.result actions] != nil) && ([self.result actions].count > 0);
158 // If you need to do additional setup after loading the view, override viewDidLoad.
159 - (void)viewDidLoad {
164 - (void)clearImageView {
165 imageView.image = nil;
166 for (UIView *view in resultPointViews) {
167 [view removeFromSuperview];
169 [resultPointViews removeAllObjects];
172 - (void)pickAndDecodeFromSource:(UIImagePickerControllerSourceType) sourceType {
175 // Create the Image Picker
176 if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
177 UIImagePickerController* picker = [[UIImagePickerController alloc] init];
178 picker.sourceType = sourceType;
179 picker.delegate = self;
180 picker.allowsImageEditing = YES; // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
182 // Picker is displayed asynchronously.
183 [self presentModalViewController:picker animated:YES];
185 NSLog(@"Attempted to pick an image with illegal source type '%d'", sourceType);
189 - (IBAction)pickAndDecode:(id) sender {
190 UIImagePickerControllerSourceType sourceType;
191 int i = [sender tag];
194 case 0: sourceType = UIImagePickerControllerSourceTypeCamera; break;
195 case 1: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; break;
196 case 2: sourceType = UIImagePickerControllerSourceTypePhotoLibrary; break;
197 default: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
199 [self pickAndDecodeFromSource:sourceType];
203 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
204 // Return YES for supported orientations
205 return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
209 - (void)didReceiveMemoryWarning {
210 [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
211 // Release anything that's not essential, such as cached data
216 [self clearImageView];
218 [actionBarItem release];
219 [cameraBarItem release];
220 [libraryBarItem release];
221 [savedPhotosBarItem release];
222 [archiveBarItem release];
225 [resultPointViews dealloc];
230 - (void)showMessage:(NSString *)message helpButton:(BOOL)showHelpButton {
232 NSLog(@"Showing message '%@' %@ help Button", message, showHelpButton ? @"with" : @"without");
235 CGSize maxSize = imageView.bounds.size;
236 if (showHelpButton) {
237 maxSize.width -= messageHelpButton.frame.size.width;
239 CGSize size = [message sizeWithFont:messageTextView.font constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
240 float height = 20.0 + fmin(100.0, size.height);
241 if (showHelpButton) {
242 height = fmax(HELP_BUTTON_HEIGHT, height);
245 CGRect messageFrame = imageView.bounds;
246 messageFrame.origin.y = CGRectGetMaxY(messageFrame) - height;
247 messageFrame.size.height = height;
248 [self.messageView setFrame:messageFrame];
249 messageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
250 CGRect messageViewBounds = [messageView bounds];
252 self.messageTextView.text = message;
253 messageTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
254 if (showHelpButton) {
255 CGRect textViewFrame;
256 CGRect helpButtonFrame;
258 CGRectDivide(messageViewBounds, &helpButtonFrame, &textViewFrame, HELP_BUTTON_WIDTH, CGRectMaxXEdge);
259 [self.messageTextView setFrame:textViewFrame];
261 [messageHelpButton setFrame:helpButtonFrame];
262 messageHelpButton.alpha = 1.0;
263 messageHelpButton.enabled = YES;
264 messageHelpButton.autoresizingMask =
265 UIViewAutoresizingFlexibleLeftMargin |
266 UIViewAutoresizingFlexibleTopMargin;
267 [messageView addSubview:messageHelpButton];
269 [messageHelpButton removeFromSuperview];
270 messageHelpButton.alpha = 0.0;
271 messageHelpButton.enabled = NO;
273 [self.messageTextView setFrame:messageViewBounds];
277 // DecoderDelegate methods
279 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image {
280 [self clearImageView];
281 [self.imageView setImage:image];
282 [self showMessage:[NSString stringWithFormat:NSLocalizedString(@"DecoderViewController MessageWhileDecodingWithDimensions", @"Decoding image (%.0fx%.0f) ..."), image.size.width, image.size.height]
286 - (void)decoder:(Decoder *)decoder
287 decodingImage:(UIImage *)image
288 usingSubset:(UIImage *)subset
289 progress:(NSString *)message {
290 [self clearImageView];
291 [self.imageView setImage:subset];
292 [self showMessage:message helpButton:NO];
295 - (void)presentResultForString:(NSString *)resultString {
296 self.result = [ResultParser parsedResultForString:resultString];
297 [self showMessage:[self.result stringForDisplay] helpButton:NO];
298 self.actions = self.result.actions;
300 NSLog(@"result has %d actions", actions ? 0 : actions.count);
302 [self updateToolbar];
305 - (void)presentResultPoints:(NSArray *)resultPoints
306 forImage:(UIImage *)image
307 usingSubset:(UIImage *)subset {
308 // simply add the points to the image view
309 imageView.image = subset;
310 for (NSValue *pointValue in resultPoints) {
311 [imageView addResultPoint:[pointValue CGPointValue]];
315 - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
316 [self presentResultForString:twoDResult.text];
318 [self presentResultPoints:twoDResult.points forImage:image usingSubset:subset];
320 // save the scan to the shared database
321 [[Database sharedDatabase] addScanWithText:twoDResult.text];
323 [self performResultAction:self];
326 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
327 [self showMessage:reason helpButton:YES];
328 [self updateToolbar];
332 - (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
333 [super willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
335 if (imageView.image) {
337 CGRect viewBounds = imageView.bounds;
338 CGSize imageSize = imageView.image.size;
339 float scale = fmin(viewBounds.size.width / imageSize.width,
340 viewBounds.size.height / imageSize.height);
341 float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
342 float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
345 for (UIView *view in resultPointViews) {
351 - (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration {
352 [super willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration];
354 if (imageView.image) {
356 CGRect viewBounds = imageView.bounds;
357 CGSize imageSize = imageView.image.size;
358 float scale = fmin(viewBounds.size.width / imageSize.width,
359 viewBounds.size.height / imageSize.height);
360 float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
361 float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
364 for (UIView *view in resultPointViews) {
370 // UIImagePickerControllerDelegate methods
372 - (void)imagePickerController:(UIImagePickerController *)picker
373 didFinishPickingImage:(UIImage *)image
374 editingInfo:(NSDictionary *)editingInfo
376 UIImage *imageToDecode = image;
378 NSLog(@"picked image size = (%f, %f)", image.size.width, image.size.height);
381 UIImage *originalImage = [editingInfo objectForKey:UIImagePickerControllerOriginalImage];
384 NSLog(@"original image size = (%f, %f)", originalImage.size.width, originalImage.size.height);
386 NSValue *cropRectValue = [editingInfo objectForKey:UIImagePickerControllerCropRect];
388 CGRect cropRect = [cropRectValue CGRectValue];
390 NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
392 UIGraphicsBeginImageContext(cropRect.size);
394 [originalImage drawAtPoint:CGPointMake(-CGRectGetMinX(cropRect),
395 -CGRectGetMinY(cropRect))];
397 imageToDecode = UIGraphicsGetImageFromCurrentImageContext();
398 UIGraphicsEndImageContext();
403 [[picker parentViewController] dismissModalViewControllerAnimated:YES];
404 [imageToDecode retain];
406 [self.decoder decodeImage:imageToDecode];
407 [imageToDecode release];
408 [self updateToolbar];
411 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
413 [picker dismissModalViewControllerAnimated:YES];
415 [self updateToolbar];
418 - (void)navigationController:(UINavigationController *)navigationController
419 didShowViewController:(UIViewController *)viewController
420 animated:(BOOL)animated {
424 - (void)navigationController:(UINavigationController *)navigationController
425 willShowViewController:(UIViewController *)viewController
426 animated:(BOOL)animated {
430 - (void)performAction:(ResultAction *)action {
431 [action performActionWithController:self shouldConfirm:NO];
434 - (void)confirmAndPerformAction:(ResultAction *)action {
435 [action performActionWithController:self shouldConfirm:YES];
439 - (IBAction)performResultAction:(id)sender {
440 if (self.result == nil) {
441 NSLog(@"no result to perform an action on!");
445 if (self.actions == nil || self.actions.count == 0) {
446 NSLog(@"result has no actions to perform!");
450 if (self.actions.count == 1) {
451 ResultAction *action = [self.actions lastObject];
453 NSLog(@"Result has the single action, (%@) '%@', performing it",
454 NSStringFromClass([action class]), [action title]);
456 [self performSelector:@selector(confirmAndPerformAction:)
461 NSLog(@"Result has multiple actions, popping up an action sheet");
463 UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithFrame:self.view.bounds];
465 for (ResultAction *action in self.actions) {
466 [actionSheet addButtonWithTitle:[action title]];
469 int cancelIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"DecoderViewController cancel button title", @"Cancel")];
470 actionSheet.cancelButtonIndex = cancelIndex;
472 actionSheet.delegate = self;
474 [actionSheet showFromToolbar:self.toolbar];
478 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
479 if (buttonIndex < self.actions.count) {
480 int actionIndex = buttonIndex;
481 ResultAction *action = [self.actions objectAtIndex:actionIndex];
482 [self performSelector:@selector(performAction:)
488 - (IBAction)showArchive:(id)sender {
489 ArchiveController *archiveController = [[ArchiveController alloc] initWithDecoderViewController:self];
490 [[self navigationController] pushViewController:archiveController animated:true];
491 [archiveController release];
494 - (void)showScan:(Scan *)scan {
495 [self clearImageView];
496 [self presentResultForString:scan.text];
497 [[self navigationController] popToViewController:self animated:YES];