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 imageView;
51 @synthesize resultPointViews;
53 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
54 if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
55 // Initialization code
56 self.title = @"ZXing";
58 Decoder *d = [[Decoder alloc] init];
62 resultPointViews = [[NSMutableArray alloc] init];
67 #define FONT_NAME @"TimesNewRomanPSMT"
68 #define FONT_SIZE 16.0
70 // Implement loadView if you want to create a view hierarchy programmatically
74 CGRect messageViewFrame = imageView.frame;
75 UITextView *mView = [[UITextView alloc] initWithFrame:messageViewFrame];
76 mView.backgroundColor = [UIColor darkGrayColor];
78 mView.editable = false;
79 mView.scrollEnabled = true;
80 mView.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE];
81 mView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
82 UIViewAutoresizingFlexibleWidth |
83 UIViewAutoresizingFlexibleTopMargin;
84 mView.textColor = [UIColor whiteColor];
85 mView.textAlignment = UITextAlignmentLeft;
86 self.messageView = mView;
89 [self.view addSubview:self.messageView];
91 [self showMessage:NSLocalizedString(@"Please take or choose a picture containing a barcode", @"")];
94 - (void) updateToolbar {
95 self.cameraBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
96 self.savedPhotosBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum];
97 self.libraryBarItem.enabled = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary];
98 self.archiveBarItem.enabled = true;
99 self.actionBarItem.enabled = (self.result != nil) && ([self.result actions] != nil) && ([self.result actions].count > 0);
103 // If you need to do additional setup after loading the view, override viewDidLoad.
104 - (void)viewDidLoad {
109 - (void)clearImageView {
110 imageView.image = nil;
111 for (UIView *view in resultPointViews) {
112 [view removeFromSuperview];
114 [resultPointViews removeAllObjects];
117 - (void)pickAndDecodeFromSource:(UIImagePickerControllerSourceType) sourceType {
119 [self clearImageView];
120 // Create the Image Picker
121 if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
122 UIImagePickerController* picker = [[UIImagePickerController alloc] init];
123 picker.sourceType = sourceType;
124 picker.delegate = self;
125 picker.allowsImageEditing = YES; // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
127 // Picker is displayed asynchronously.
128 [self presentModalViewController:picker animated:YES];
130 NSLog(@"Attempted to pick an image with illegal source type '%d'", sourceType);
134 - (IBAction)pickAndDecode:(id) sender {
135 UIImagePickerControllerSourceType sourceType;
136 int i = [sender tag];
139 case 0: sourceType = UIImagePickerControllerSourceTypeCamera; break;
140 case 1: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; break;
141 case 2: sourceType = UIImagePickerControllerSourceTypePhotoLibrary; break;
142 default: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
144 [self pickAndDecodeFromSource:sourceType];
148 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
149 // Return YES for supported orientations
150 return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
154 - (void)didReceiveMemoryWarning {
155 [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
156 // Release anything that's not essential, such as cached data
161 [self clearImageView];
163 [actionBarItem release];
164 [cameraBarItem release];
165 [libraryBarItem release];
166 [savedPhotosBarItem release];
167 [archiveBarItem release];
170 [resultPointViews dealloc];
175 - (void)showMessage:(NSString *)message {
177 NSLog(@"Showing message '%@'", message);
180 CGSize maxSize = imageView.bounds.size;
181 CGSize size = [message sizeWithFont:messageView.font constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
182 float height = 20.0 + fmin(100.0, size.height);
184 CGRect messageFrame = imageView.bounds;
185 messageFrame.origin.y = CGRectGetMaxY(messageFrame) - height;
186 messageFrame.size.height = height;
187 self.messageView.text = message;
188 [self.messageView setFrame:messageFrame];
191 // DecoderDelegate methods
193 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image {
194 [self clearImageView];
195 [self.imageView setImage:image];
196 [self showMessage:[NSString stringWithFormat:NSLocalizedString(@"Decoding image (%.0fx%.0f) ...", @"shown while image is decoding"), image.size.width, image.size.height]];
199 - (void)decoder:(Decoder *)decoder
200 decodingImage:(UIImage *)image
201 usingSubset:(UIImage *)subset
202 progress:(NSString *)message {
203 [self clearImageView];
204 [self.imageView setImage:subset];
205 [self showMessage:message];
208 - (void)presentResultForString:(NSString *)resultString {
209 self.result = [ResultParser parsedResultForString:resultString];
210 [self showMessage:[self.result stringForDisplay]];
211 self.actions = self.result.actions;
213 NSLog(@"result has %d actions", actions ? 0 : actions.count);
215 [self updateToolbar];
218 - (void)presentResultPoints:(NSArray *)resultPoints
219 forImage:(UIImage *)image
220 usingSubset:(UIImage *)subset {
221 // simply add the points to the image view
222 imageView.image = subset;
223 for (NSValue *pointValue in resultPoints) {
224 [imageView addResultPoint:[pointValue CGPointValue]];
228 - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
229 [self presentResultForString:twoDResult.text];
231 [self presentResultPoints:twoDResult.points forImage:image usingSubset:subset];
233 // save the scan to the shared database
234 [[Database sharedDatabase] addScanWithText:twoDResult.text];
236 [self performResultAction:self];
239 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
240 [self showMessage:reason];
241 [self updateToolbar];
245 - (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
246 [super willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
248 if (imageView.image) {
250 CGRect viewBounds = imageView.bounds;
251 CGSize imageSize = imageView.image.size;
252 float scale = fmin(viewBounds.size.width / imageSize.width,
253 viewBounds.size.height / imageSize.height);
254 float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
255 float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
258 for (UIView *view in resultPointViews) {
264 - (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration {
265 [super willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration];
267 if (imageView.image) {
269 CGRect viewBounds = imageView.bounds;
270 CGSize imageSize = imageView.image.size;
271 float scale = fmin(viewBounds.size.width / imageSize.width,
272 viewBounds.size.height / imageSize.height);
273 float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
274 float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
277 for (UIView *view in resultPointViews) {
283 // UIImagePickerControllerDelegate methods
285 - (void)imagePickerController:(UIImagePickerController *)picker
286 didFinishPickingImage:(UIImage *)image
287 editingInfo:(NSDictionary *)editingInfo
289 UIImage *imageToDecode = image;
291 NSLog(@"picked image size = (%f, %f)", image.size.width, image.size.height);
294 UIImage *originalImage = [editingInfo objectForKey:UIImagePickerControllerOriginalImage];
297 NSLog(@"original image size = (%f, %f)", originalImage.size.width, originalImage.size.height);
299 NSValue *cropRectValue = [editingInfo objectForKey:UIImagePickerControllerCropRect];
301 CGRect cropRect = [cropRectValue CGRectValue];
303 NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
305 UIGraphicsBeginImageContext(cropRect.size);
307 [originalImage drawAtPoint:CGPointMake(-CGRectGetMinX(cropRect),
308 -CGRectGetMinY(cropRect))];
310 imageToDecode = UIGraphicsGetImageFromCurrentImageContext();
311 UIGraphicsEndImageContext();
316 [[picker parentViewController] dismissModalViewControllerAnimated:YES];
317 [imageToDecode retain];
319 [self.decoder decodeImage:imageToDecode];
320 [imageToDecode release];
321 [self updateToolbar];
324 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
326 [picker dismissModalViewControllerAnimated:YES];
328 [self updateToolbar];
331 - (void)navigationController:(UINavigationController *)navigationController
332 didShowViewController:(UIViewController *)viewController
333 animated:(BOOL)animated {
337 - (void)navigationController:(UINavigationController *)navigationController
338 willShowViewController:(UIViewController *)viewController
339 animated:(BOOL)animated {
343 - (void)performAction:(ResultAction *)action {
344 [action performActionWithController:self shouldConfirm:NO];
347 - (void)confirmAndPerformAction:(ResultAction *)action {
348 [action performActionWithController:self shouldConfirm:YES];
352 - (IBAction)performResultAction:(id)sender {
353 if (self.result == nil) {
354 NSLog(@"no result to perform an action on!");
358 if (self.actions == nil || self.actions.count == 0) {
359 NSLog(@"result has no actions to perform!");
363 if (self.actions.count == 1) {
364 ResultAction *action = [self.actions lastObject];
366 NSLog(@"Result has the single action, (%@) '%@', performing it",
367 NSStringFromClass([action class]), [action title]);
369 [self performSelector:@selector(confirmAndPerformAction:)
374 NSLog(@"Result has multiple actions, popping up an action sheet");
376 UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithFrame:self.view.bounds];
378 for (ResultAction *action in self.actions) {
379 [actionSheet addButtonWithTitle:[action title]];
382 int cancelIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
383 actionSheet.cancelButtonIndex = cancelIndex;
385 actionSheet.delegate = self;
387 [actionSheet showFromToolbar:self.toolbar];
391 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
392 if (buttonIndex < self.actions.count) {
393 int actionIndex = buttonIndex;
394 ResultAction *action = [self.actions objectAtIndex:actionIndex];
395 [self performSelector:@selector(performAction:)
401 - (IBAction)showArchive:(id)sender {
402 ArchiveController *archiveController = [[ArchiveController alloc] initWithDecoderViewController:self];
403 [[self navigationController] pushViewController:archiveController animated:true];
404 [archiveController release];
407 - (void)showScan:(Scan *)scan {
408 [self clearImageView];
409 [self presentResultForString:scan.text];
410 [[self navigationController] popToViewController:self animated:YES];