#import "Scan.h"
#import "TwoDDecoderResult.h"
+// Michael Jurewitz, Dec 16, 2009 6:32 PM writes:
+// https://devforums.apple.com/message/149553
+// Notice Regarding UIGetScreenImage()
+// After carefully considering the issue, Apple is now allowing applications to
+// use the function UIGetScreenImage() to programmatically capture the current
+// screen contents.
+// Note that a future release of iPhone OS may provide a public API equivalent
+// of this functionality. At such time, all applications using
+// UIGetScreenImage() will be required to adopt the public API.
+CGImageRef MyCGImageCopyScreenContents(void) {
+ extern CGImageRef UIGetScreenImage(void);
+ return UIGetScreenImage(); /* already retained */
+}
+
+@interface DecoderViewController()
+- (void)takeScreenshot;
+@end
+
@implementation DecoderViewController
@synthesize cameraBarItem;
@synthesize messageTextView;
@synthesize messageHelpButton;
@synthesize imageView;
+@synthesize picker;
@synthesize toolbar;
@synthesize decoder;
@synthesize resultPointViews;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
- if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
- // Initialization code
- self.title = NSLocalizedString(@"DecoderViewController AppTitle", @"Barcode Scanner");
-
+ if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
+ // Initialization code
+ self.title =
+ NSLocalizedString(@"DecoderViewController AppTitle", @"Barcode Scanner");
+
Decoder *d = [[Decoder alloc] init];
self.decoder = d;
d.delegate = self;
[d release];
resultPointViews = [[NSMutableArray alloc] init];
- }
- return self;
+ }
+ return self;
}
- (void) messageReady:(id)sender {
- (void) showHints:(id)sender {
NSLog(@"Showing Hints!");
-
- MessageViewController *hintsController =
+
+ MessageViewController *hintsController =
[[MessageViewController alloc] initWithMessageFilename:@"Hints"
- target:self
- onSuccess:@selector(messageReady:)
+ target:self
+ onSuccess:@selector(messageReady:)
onFailure:@selector(messageFailed:)];
- hintsController.title = NSLocalizedString(@"DecoderViewController Hints MessageViewController title", @"Hints");
+ hintsController.title =
+ NSLocalizedString(@"DecoderViewController Hints MessageViewController title", @"Hints");
[hintsController view];
}
- (void) showAbout:(id)sender {
NSLog(@"Showing About!");
-
- MessageViewController *aboutController =
+
+ MessageViewController *aboutController =
[[MessageViewController alloc] initWithMessageFilename:@"About"
- target:self
- onSuccess:@selector(messageReady:)
+ target:self
+ onSuccess:@selector(messageReady:)
onFailure:@selector(messageFailed:)];
- aboutController.title = NSLocalizedString(@"DecoderViewController About MessageViewController title", @"About");
+ aboutController.title =
+ NSLocalizedString(@"DecoderViewController About MessageViewController title", @"About");
[aboutController view];
}
-
+
#define HELP_BUTTON_WIDTH (44.0)
#define HELP_BUTTON_HEIGHT (55.0)
self.result = nil;
[self clearImageView];
[self updateToolbar];
- [self showMessage:NSLocalizedString(@"DecoderViewController take or choose picture", @"Please take or choose a picture containing a barcode") helpButton:YES];
+ [self showMessage:NSLocalizedString(@"DecoderViewController take or choose picture",
+ @"Please take or choose a picture containing a barcode") helpButton:YES];
}
// Implement loadView if you want to create a view hierarchy programmatically
- (void)loadView {
[super loadView];
-
+
CGRect messageViewFrame = imageView.frame;
UIView *mView = [[UIView alloc] initWithFrame:messageViewFrame];
mView.backgroundColor = [UIColor darkGrayColor];
mView.alpha = 0.9;
- mView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
+ mView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleTopMargin;
-
+
UITextView *mTextView = [[UITextView alloc] initWithFrame:messageViewFrame];
- mTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
+ mTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFlexibleWidth;
mTextView.editable = false;
mTextView.scrollEnabled = true;
UIButton *mHelpButton = [[UIButton buttonWithType:UIButtonTypeInfoLight] retain];
mHelpButton.frame = CGRectMake(messageViewFrame.size.width - HELP_BUTTON_WIDTH, 0.0, HELP_BUTTON_WIDTH, HELP_BUTTON_HEIGHT);
-
+
mHelpButton.backgroundColor = [UIColor clearColor];
[mHelpButton setUserInteractionEnabled:YES];
[mHelpButton addTarget:self action:@selector(showHints:) forControlEvents:UIControlEventTouchUpInside];
self.messageHelpButton = mHelpButton;
[mHelpButton release];
-
+
self.messageTextView = mTextView;
[mTextView release];
-
+
self.messageView = mView;
[mView release];
-
+
[self.view addSubview:self.messageView];
-
+
// add the 'About' button at the top-right of the navigation bar
- UIBarButtonItem *aboutButton =
- [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"DecoderViewController about button title", @"About")
+ UIBarButtonItem *aboutButton =
+ [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"DecoderViewController about button title", @"About")
style:UIBarButtonItemStyleBordered
- target:self
+ target:self
action:@selector(showAbout:)];
self.navigationItem.rightBarButtonItem = aboutButton;
[aboutButton release];
-
+
[self reset];
}
// Create the Image Picker
if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
- UIImagePickerController* picker = [[UIImagePickerController alloc] init];
- picker.sourceType = sourceType;
- picker.delegate = self;
- picker.allowsImageEditing = YES; // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
-
+ UIImagePickerController* aPicker =
+ [[[UIImagePickerController alloc] init] autorelease];
+ aPicker.sourceType = sourceType;
+ aPicker.delegate = self;
+ self.picker = aPicker;
+
+ // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
+ BOOL isCamera = (sourceType == UIImagePickerControllerSourceTypeCamera);
+ if ([picker respondsToSelector:@selector(setAllowsEditing:)]) {
+ // not in 3.0
+ [picker setAllowsEditing:!isCamera];
+ }
+ if (isCamera) {
+ if ([picker respondsToSelector:@selector(setShowsCameraControls:)]) {
+ [picker setShowsCameraControls:NO];
+ UIButton *cancelButton =
+ [UIButton buttonWithType:UIButtonTypeRoundedRect];
+ NSString *cancelString =
+ NSLocalizedString(@"DecoderViewController cancel button title", @"");
+ CGFloat height = [UIFont systemFontSize];
+ CGSize size =
+ [cancelString sizeWithFont:[UIFont systemFontOfSize:height]];
+ [cancelButton setTitle:cancelString forState:UIControlStateNormal];
+ CGRect appFrame = [[UIScreen mainScreen] bounds];
+ static const int kMargin = 10;
+ static const int kInternalXMargin = 10;
+ static const int kInternalYMargin = 10;
+ CGRect frame = CGRectMake(kMargin,
+ appFrame.size.height - (height + 2*kInternalYMargin + kMargin),
+ 2*kInternalXMargin + size.width,
+ height + 2*kInternalYMargin);
+ [cancelButton setFrame:frame];
+ [cancelButton addTarget:self
+ action:@selector(cancel:)
+ forControlEvents:UIControlEventTouchUpInside];
+ picker.cameraOverlayView = cancelButton;
+ // The camera takes quite a while to start up. Hence the 2 second delay.
+ [self performSelector:@selector(takeScreenshot)
+ withObject:nil
+ afterDelay:2.0];
+ }
+ }
+
// Picker is displayed asynchronously.
[self presentModalViewController:picker animated:YES];
} else {
- (IBAction)pickAndDecode:(id) sender {
UIImagePickerControllerSourceType sourceType;
int i = [sender tag];
-
+
switch (i) {
case 0: sourceType = UIImagePickerControllerSourceTypeCamera; break;
case 1: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; break;
[savedPhotosBarItem release];
[archiveBarItem release];
[toolbar release];
- [actions dealloc];
- [resultPointViews dealloc];
-
- [super dealloc];
+ [picker release];
+ [actions release];
+ [resultPointViews release];
+
+ [super dealloc];
}
- (void)showMessage:(NSString *)message helpButton:(BOOL)showHelpButton {
#ifdef DEBUG
NSLog(@"Showing message '%@' %@ help Button", message, showHelpButton ? @"with" : @"without");
#endif
-
+
CGSize imageMaxSize = imageView.bounds.size;
if (showHelpButton) {
imageMaxSize.width -= messageHelpButton.frame.size.width;
if (showHelpButton) {
CGRect textViewFrame;
CGRect helpButtonFrame;
-
+
CGRectDivide(messageViewBounds, &helpButtonFrame, &textViewFrame, HELP_BUTTON_WIDTH, CGRectMaxXEdge);
[self.messageTextView setFrame:textViewFrame];
-
+
[messageHelpButton setFrame:helpButtonFrame];
messageHelpButton.alpha = 1.0;
messageHelpButton.enabled = YES;
- messageHelpButton.autoresizingMask =
- UIViewAutoresizingFlexibleLeftMargin |
+ messageHelpButton.autoresizingMask =
+ UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleTopMargin;
[messageView addSubview:messageHelpButton];
} else {
helpButton:NO];
}
-- (void)decoder:(Decoder *)decoder
- decodingImage:(UIImage *)image
+- (void)decoder:(Decoder *)decoder
+ decodingImage:(UIImage *)image
usingSubset:(UIImage *)subset
progress:(NSString *)message {
[self clearImageView];
NSLog(@"result has %d actions", actions ? 0 : actions.count);
#endif
[self updateToolbar];
-}
+}
-- (void)presentResultPoints:(NSArray *)resultPoints
+- (void)presentResultPoints:(NSArray *)resultPoints
forImage:(UIImage *)image
usingSubset:(UIImage *)subset {
// simply add the points to the image view
}
- (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
+ self.picker = nil;
[self presentResultForString:twoDResult.text];
-
+
[self presentResultPoints:twoDResult.points forImage:image usingSubset:subset];
-
+
// save the scan to the shared database
[[Database sharedDatabase] addScanWithText:twoDResult.text];
-
+
[self performResultAction:self];
}
- (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
- [self showMessage:reason helpButton:YES];
- [self updateToolbar];
+ if (self.picker && UIImagePickerControllerSourceTypeCamera == self.picker.sourceType) {
+ // If we are using the camera, and the user hasn't manually cancelled,
+ // take another snapshot and try to decode it.
+ [self takeScreenshot];
+ } else {
+ [self showMessage:reason helpButton:YES];
+ [self updateToolbar];
+ }
}
- (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
-
+
if (imageView.image) {
/*
CGRect viewBounds = imageView.bounds;
float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
*/
-
+
for (UIView *view in resultPointViews) {
view.alpha = 0.0;
}
- }
+ }
}
- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration {
float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
*/
-
+
for (UIView *view in resultPointViews) {
view.alpha = 1.0;
}
- }
+ }
+}
+
+- (void)cancel:(id)sender {
+ self.picker = nil;
+}
+
+- (void)takeScreenshot {
+ if (picker) {
+ CGImageRef cgScreen = MyCGImageCopyScreenContents();
+ if (cgScreen) {
+ CGRect croppedFrame = CGRectMake(0, 0, CGImageGetWidth(cgScreen),
+ CGImageGetHeight(cgScreen) - (10+toolbar.bounds.size.height));
+ CGImageRef cgCropped = CGImageCreateWithImageInRect(cgScreen, croppedFrame);
+ if (cgCropped) {
+ UIImage *screenshot = [UIImage imageWithCGImage:cgCropped];
+ CGImageRelease(cgCropped);
+ [self.decoder decodeImage:screenshot];
+ }
+ CGImageRelease(cgScreen);
+ }
+ }
}
// UIImagePickerControllerDelegate methods
-- (void)imagePickerController:(UIImagePickerController *)picker
- didFinishPickingImage:(UIImage *)image
- editingInfo:(NSDictionary *)editingInfo
-{
- UIImage *imageToDecode = image;
- CGSize size = [image size];
- CGRect cropRect = CGRectMake(0.0, 0.0, size.width, size.height);
-
+- (void)imagePickerController:(UIImagePickerController *)aPicker
+didFinishPickingMediaWithInfo:(NSDictionary *)info {
+ UIImage *imageToDecode =
+ [info objectForKey:UIImagePickerControllerEditedImage];
+ if (!imageToDecode) {
+ imageToDecode = [info objectForKey:UIImagePickerControllerOriginalImage];
+ }
+ CGSize size = [imageToDecode size];
+ CGRect cropRect = CGRectMake(0.0, 0.0, size.width, size.height);
+
#ifdef DEBUG
NSLog(@"picked image size = (%f, %f)", size.width, size.height);
#endif
NSString *systemVersion = [[UIDevice currentDevice] systemVersion];
-
- if (editingInfo) {
- UIImage *originalImage = [editingInfo objectForKey:UIImagePickerControllerOriginalImage];
+
+ NSValue *cropRectValue = [info objectForKey:UIImagePickerControllerCropRect];
+ if (cropRectValue) {
+ UIImage *originalImage = [info objectForKey:UIImagePickerControllerOriginalImage];
if (originalImage) {
#ifdef DEBUG
NSLog(@"original image size = (%f, %f)", originalImage.size.width, originalImage.size.height);
#endif
- NSValue *cropRectValue = [editingInfo objectForKey:UIImagePickerControllerCropRect];
- if (cropRectValue) {
- cropRect = [cropRectValue CGRectValue];
+ cropRect = [cropRectValue CGRectValue];
#ifdef DEBUG
- NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
+ NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
#endif
- if (([picker sourceType] == UIImagePickerControllerSourceTypeSavedPhotosAlbum) &&
- [@"2.1" isEqualToString:systemVersion]) {
- // adjust crop rect to work around bug in iPhone OS 2.1 when selecting from the photo roll
- cropRect.origin.x *= 2.5;
- cropRect.origin.y *= 2.5;
- cropRect.size.width *= 2.5;
- cropRect.size.height *= 2.5;
+ if (([picker sourceType] == UIImagePickerControllerSourceTypeSavedPhotosAlbum) &&
+ [@"2.1" isEqualToString:systemVersion]) {
+ // adjust crop rect to work around bug in iPhone OS 2.1 when selecting from the photo roll
+ cropRect.origin.x *= 2.5;
+ cropRect.origin.y *= 2.5;
+ cropRect.size.width *= 2.5;
+ cropRect.size.height *= 2.5;
#ifdef DEBUG
- NSLog(@"2.1-adjusted crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
+ NSLog(@"2.1-adjusted crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
#endif
- }
-
- imageToDecode = originalImage;
}
+
+ imageToDecode = originalImage;
}
}
-
- [[picker parentViewController] dismissModalViewControllerAnimated:YES];
+
[imageToDecode retain];
- [picker release];
+ self.picker = nil;
[self.decoder decodeImage:imageToDecode cropRect:cropRect];
[imageToDecode release];
- [self updateToolbar];
}
-- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
-{
- [picker dismissModalViewControllerAnimated:YES];
- [picker release];
- [self updateToolbar];
+
+- (void)imagePickerControllerDidCancel:(UIImagePickerController *)aPicker {
+ self.picker = nil;
}
-- (void)navigationController:(UINavigationController *)navigationController
- didShowViewController:(UIViewController *)viewController
+- (void)setPicker:(UIImagePickerController *)aPicker {
+ if (picker != aPicker) {
+ [picker dismissModalViewControllerAnimated:YES];
+ picker = [aPicker retain];
+ [self updateToolbar];
+ }
+}
+
+- (void)navigationController:(UINavigationController *)navigationController
+ didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
// no-op
}
-- (void)navigationController:(UINavigationController *)navigationController
- willShowViewController:(UIViewController *)viewController
+- (void)navigationController:(UINavigationController *)navigationController
+ willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
// no-op
}
NSLog(@"no result to perform an action on!");
return;
}
-
+
if (self.actions == nil || self.actions.count == 0) {
NSLog(@"result has no actions to perform!");
return;
}
-
+
if (self.actions.count == 1) {
ResultAction *action = [self.actions lastObject];
#ifdef DEBUG
NSLog(@"Result has the single action, (%@) '%@', performing it",
NSStringFromClass([action class]), [action title]);
#endif
- [self performSelector:@selector(confirmAndPerformAction:)
- withObject:action
+ [self performSelector:@selector(confirmAndPerformAction:)
+ withObject:action
afterDelay:0.0];
} else {
#ifdef DEBUG
NSLog(@"Result has multiple actions, popping up an action sheet");
#endif
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithFrame:self.view.bounds];
-
+
for (ResultAction *action in self.actions) {
[actionSheet addButtonWithTitle:[action title]];
}
-
+
int cancelIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"DecoderViewController cancel button title", @"Cancel")];
actionSheet.cancelButtonIndex = cancelIndex;
-
+
actionSheet.delegate = self;
-
+
[actionSheet showFromToolbar:self.toolbar];
}
}
if (buttonIndex < self.actions.count) {
int actionIndex = buttonIndex;
ResultAction *action = [self.actions objectAtIndex:actionIndex];
- [self performSelector:@selector(performAction:)
- withObject:action
+ [self performSelector:@selector(performAction:)
+ withObject:action
afterDelay:0.0];
}
}