removed my proj files... get generated for each user locally.
[zxing.git] / iphone / Classes / DecoderViewController.m
index 6fb2d86..26da41d 100644 (file)
 #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;
@@ -44,6 +62,7 @@
 @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];
   }
 }