Commit 360b0079 authored by Chok's avatar Chok
Browse files

chok: 8.1.0 commit

parent bf07bdf3
Pipeline #22 failed with stages
repositories{
jcenter()
flatDir{
dirs 'libs'
}
}
dependencies {
implementation(name:'barcodescanner-release-2.1.5', ext:'aar')
}
android {
packagingOptions {
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
}
/**
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) Matt Kane 2010
* Copyright (c) 2011, IBM Corporation
* Copyright (c) 2013, Maciej Nux Jaros
*/
package com.phonegap.plugins.barcodescanner;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.content.pm.PackageManager;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.apache.cordova.PermissionHelper;
import com.google.zxing.client.android.CaptureActivity;
import com.google.zxing.client.android.encode.EncodeActivity;
import com.google.zxing.client.android.Intents;
/**
* This calls out to the ZXing barcode reader and returns the result.
*
* @sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
*/
public class BarcodeScanner extends CordovaPlugin {
public static final int REQUEST_CODE = 0x0ba7c;
private static final String SCAN = "scan";
private static final String ENCODE = "encode";
private static final String CANCELLED = "cancelled";
private static final String FORMAT = "format";
private static final String TEXT = "text";
private static final String DATA = "data";
private static final String TYPE = "type";
private static final String PREFER_FRONTCAMERA = "preferFrontCamera";
private static final String ORIENTATION = "orientation";
private static final String SHOW_FLIP_CAMERA_BUTTON = "showFlipCameraButton";
private static final String RESULTDISPLAY_DURATION = "resultDisplayDuration";
private static final String SHOW_TORCH_BUTTON = "showTorchButton";
private static final String TORCH_ON = "torchOn";
private static final String SAVE_HISTORY = "saveHistory";
private static final String DISABLE_BEEP = "disableSuccessBeep";
private static final String FORMATS = "formats";
private static final String PROMPT = "prompt";
private static final String TEXT_TYPE = "TEXT_TYPE";
private static final String EMAIL_TYPE = "EMAIL_TYPE";
private static final String PHONE_TYPE = "PHONE_TYPE";
private static final String SMS_TYPE = "SMS_TYPE";
private static final String LOG_TAG = "BarcodeScanner";
private String [] permissions = { Manifest.permission.CAMERA };
private JSONArray requestArgs;
private CallbackContext callbackContext;
/**
* Constructor.
*/
public BarcodeScanner() {
}
/**
* Executes the request.
*
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
* cordova.getThreadPool().execute(runnable);
*
* To run on the UI thread, use:
* cordova.getActivity().runOnUiThread(runnable);
*
* @param action The action to execute.
* @param args The exec() arguments.
* @param callbackContext The callback context used when calling back into JavaScript.
* @return Whether the action was valid.
*
* @sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
*/
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
this.callbackContext = callbackContext;
this.requestArgs = args;
if (action.equals(ENCODE)) {
JSONObject obj = args.optJSONObject(0);
if (obj != null) {
String type = obj.optString(TYPE);
String data = obj.optString(DATA);
// If the type is null then force the type to text
if (type == null) {
type = TEXT_TYPE;
}
if (data == null) {
callbackContext.error("User did not specify data to encode");
return true;
}
encode(type, data);
} else {
callbackContext.error("User did not specify data to encode");
return true;
}
} else if (action.equals(SCAN)) {
//android permission auto add
if(!hasPermisssion()) {
requestPermissions(0);
} else {
scan(args);
}
} else {
return false;
}
return true;
}
/**
* Starts an intent to scan and decode a barcode.
*/
public void scan(final JSONArray args) {
final CordovaPlugin that = this;
cordova.getThreadPool().execute(new Runnable() {
public void run() {
Intent intentScan = new Intent(that.cordova.getActivity().getBaseContext(), CaptureActivity.class);
intentScan.setAction(Intents.Scan.ACTION);
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
// add config as intent extras
if (args.length() > 0) {
JSONObject obj;
JSONArray names;
String key;
Object value;
for (int i = 0; i < args.length(); i++) {
try {
obj = args.getJSONObject(i);
} catch (JSONException e) {
Log.i("CordovaLog", e.getLocalizedMessage());
continue;
}
names = obj.names();
for (int j = 0; j < names.length(); j++) {
try {
key = names.getString(j);
value = obj.get(key);
if (value instanceof Integer) {
intentScan.putExtra(key, (Integer) value);
} else if (value instanceof String) {
intentScan.putExtra(key, (String) value);
}
} catch (JSONException e) {
Log.i("CordovaLog", e.getLocalizedMessage());
}
}
intentScan.putExtra(Intents.Scan.CAMERA_ID, obj.optBoolean(PREFER_FRONTCAMERA, false) ? 1 : 0);
intentScan.putExtra(Intents.Scan.SHOW_FLIP_CAMERA_BUTTON, obj.optBoolean(SHOW_FLIP_CAMERA_BUTTON, false));
intentScan.putExtra(Intents.Scan.SHOW_TORCH_BUTTON, obj.optBoolean(SHOW_TORCH_BUTTON, false));
intentScan.putExtra(Intents.Scan.TORCH_ON, obj.optBoolean(TORCH_ON, false));
intentScan.putExtra(Intents.Scan.SAVE_HISTORY, obj.optBoolean(SAVE_HISTORY, false));
boolean beep = obj.optBoolean(DISABLE_BEEP, false);
intentScan.putExtra(Intents.Scan.BEEP_ON_SCAN, !beep);
if (obj.has(RESULTDISPLAY_DURATION)) {
intentScan.putExtra(Intents.Scan.RESULT_DISPLAY_DURATION_MS, "" + obj.optLong(RESULTDISPLAY_DURATION));
}
if (obj.has(FORMATS)) {
intentScan.putExtra(Intents.Scan.FORMATS, obj.optString(FORMATS));
}
if (obj.has(PROMPT)) {
intentScan.putExtra(Intents.Scan.PROMPT_MESSAGE, obj.optString(PROMPT));
}
if (obj.has(ORIENTATION)) {
intentScan.putExtra(Intents.Scan.ORIENTATION_LOCK, obj.optString(ORIENTATION));
}
}
}
// avoid calling other phonegap apps
intentScan.setPackage(that.cordova.getActivity().getApplicationContext().getPackageName());
that.cordova.startActivityForResult(that, intentScan, REQUEST_CODE);
}
});
}
/**
* Called when the barcode scanner intent completes.
*
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == REQUEST_CODE && this.callbackContext != null) {
if (resultCode == Activity.RESULT_OK) {
JSONObject obj = new JSONObject();
try {
obj.put(TEXT, intent.getStringExtra("SCAN_RESULT"));
obj.put(FORMAT, intent.getStringExtra("SCAN_RESULT_FORMAT"));
obj.put(CANCELLED, false);
} catch (JSONException e) {
Log.d(LOG_TAG, "This should never happen");
}
//this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback);
this.callbackContext.success(obj);
} else if (resultCode == Activity.RESULT_CANCELED) {
JSONObject obj = new JSONObject();
try {
obj.put(TEXT, "");
obj.put(FORMAT, "");
obj.put(CANCELLED, true);
} catch (JSONException e) {
Log.d(LOG_TAG, "This should never happen");
}
//this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback);
this.callbackContext.success(obj);
} else {
//this.error(new PluginResult(PluginResult.Status.ERROR), this.callback);
this.callbackContext.error("Unexpected error");
}
}
}
/**
* Initiates a barcode encode.
*
* @param type Endoiding type.
* @param data The data to encode in the bar code.
*/
public void encode(String type, String data) {
Intent intentEncode = new Intent(this.cordova.getActivity().getBaseContext(), EncodeActivity.class);
intentEncode.setAction(Intents.Encode.ACTION);
intentEncode.putExtra(Intents.Encode.TYPE, type);
intentEncode.putExtra(Intents.Encode.DATA, data);
// avoid calling other phonegap apps
intentEncode.setPackage(this.cordova.getActivity().getApplicationContext().getPackageName());
this.cordova.getActivity().startActivity(intentEncode);
}
/**
* check application's permissions
*/
public boolean hasPermisssion() {
for(String p : permissions)
{
if(!PermissionHelper.hasPermission(this, p))
{
return false;
}
}
return true;
}
/**
* We override this so that we can access the permissions variable, which no longer exists in
* the parent class, since we can't initialize it reliably in the constructor!
*
* @param requestCode The code to get request action
*/
public void requestPermissions(int requestCode)
{
PermissionHelper.requestPermissions(this, requestCode, permissions);
}
/**
* processes the result of permission request
*
* @param requestCode The code to get request action
* @param permissions The collection of permissions
* @param grantResults The result of grant
*/
public void onRequestPermissionResult(int requestCode, String[] permissions,
int[] grantResults) throws JSONException
{
PluginResult result;
for (int r : grantResults) {
if (r == PackageManager.PERMISSION_DENIED) {
Log.d(LOG_TAG, "Permission Denied!");
result = new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION);
this.callbackContext.sendPluginResult(result);
return;
}
}
switch(requestCode)
{
case 0:
scan(this.requestArgs);
break;
}
}
/**
* This plugin launches an external Activity when the camera is opened, so we
* need to implement the save/restore API in case the Activity gets killed
* by the OS while it's in the background.
*/
public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {
this.callbackContext = callbackContext;
}
}
function scan(success, error) {
var code = window.prompt("Enter barcode value (empty value will fire the error handler):");
if(code) {
var result = {
text:code,
format:"Fake",
cancelled:false
};
success(result);
} else {
error("No barcode");
}
}
function encode(type, data, success, errorCallback) {
success();
}
module.exports = {
scan: scan,
encode: encode
};
require("cordova/exec/proxy").add("BarcodeScanner",module.exports);
\ No newline at end of file
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright 2011 Matt Kane. All rights reserved.
* Copyright (c) 2011, IBM Corporation
*/
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <Cordova/CDVPlugin.h>
//------------------------------------------------------------------------------
// Delegate to handle orientation functions
//------------------------------------------------------------------------------
@protocol CDVBarcodeScannerOrientationDelegate <NSObject>
- (NSUInteger)supportedInterfaceOrientations;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- (BOOL)shouldAutorotate;
@end
//------------------------------------------------------------------------------
// Adds a shutter button to the UI, and changes the scan from continuous to
// only performing a scan when you click the shutter button. For testing.
//------------------------------------------------------------------------------
#define USE_SHUTTER 0
//------------------------------------------------------------------------------
@class CDVbcsProcessor;
@class CDVbcsViewController;
//------------------------------------------------------------------------------
// plugin class
//------------------------------------------------------------------------------
@interface CDVBarcodeScanner : CDVPlugin {}
- (NSString*)isScanNotPossible;
- (void)scan:(CDVInvokedUrlCommand*)command;
- (void)encode:(CDVInvokedUrlCommand*)command;
- (void)returnImage:(NSString*)filePath format:(NSString*)format callback:(NSString*)callback;
- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback;
- (void)returnError:(NSString*)message callback:(NSString*)callback;
@end
//------------------------------------------------------------------------------
// class that does the grunt work
//------------------------------------------------------------------------------
@interface CDVbcsProcessor : NSObject <AVCaptureMetadataOutputObjectsDelegate> {}
@property (nonatomic, retain) CDVBarcodeScanner* plugin;
@property (nonatomic, retain) NSString* callback;
@property (nonatomic, retain) UIViewController* parentViewController;
@property (nonatomic, retain) CDVbcsViewController* viewController;
@property (nonatomic, retain) AVCaptureSession* captureSession;
@property (nonatomic, retain) AVCaptureVideoPreviewLayer* previewLayer;
@property (nonatomic, retain) NSString* alternateXib;
@property (nonatomic, retain) NSMutableArray* results;
@property (nonatomic, retain) NSString* formats;
@property (nonatomic) BOOL is1D;
@property (nonatomic) BOOL is2D;
@property (nonatomic) BOOL capturing;
@property (nonatomic) BOOL isFrontCamera;
@property (nonatomic) BOOL isShowFlipCameraButton;
@property (nonatomic) BOOL isShowTorchButton;
@property (nonatomic) BOOL isFlipped;
@property (nonatomic) BOOL isTransitionAnimated;
@property (nonatomic) BOOL isSuccessBeepEnabled;
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback parentViewController:(UIViewController*)parentViewController alterateOverlayXib:(NSString *)alternateXib;
- (void)scanBarcode;
- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format;
- (void)barcodeScanFailed:(NSString*)message;
- (void)barcodeScanCancelled;
- (void)openDialog;
- (NSString*)setUpCaptureSession;
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection;
@end
//------------------------------------------------------------------------------
// Qr encoder processor
//------------------------------------------------------------------------------
@interface CDVqrProcessor: NSObject
@property (nonatomic, retain) CDVBarcodeScanner* plugin;
@property (nonatomic, retain) NSString* callback;
@property (nonatomic, retain) NSString* stringToEncode;
@property NSInteger size;
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback stringToEncode:(NSString*)stringToEncode;
- (void)generateImage;
@end
//------------------------------------------------------------------------------
// view controller for the ui
//------------------------------------------------------------------------------
@interface CDVbcsViewController : UIViewController <CDVBarcodeScannerOrientationDelegate> {}
@property (nonatomic, retain) CDVbcsProcessor* processor;
@property (nonatomic, retain) NSString* alternateXib;
@property (nonatomic) BOOL shutterPressed;
@property (nonatomic, retain) IBOutlet UIView* overlayView;
@property (nonatomic, retain) UIToolbar * toolbar;
@property (nonatomic, retain) UIView * reticleView;
// unsafe_unretained is equivalent to assign - used to prevent retain cycles in the property below
@property (nonatomic, unsafe_unretained) id orientationDelegate;
- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib;
- (void)startCapturing;
- (UIView*)buildOverlayView;
- (UIImage*)buildReticleImage;
- (void)shutterButtonPressed;
- (IBAction)cancelButtonPressed:(id)sender;
- (IBAction)flipCameraButtonPressed:(id)sender;
- (IBAction)torchButtonPressed:(id)sender;
@end
//------------------------------------------------------------------------------
// plugin class
//------------------------------------------------------------------------------
@implementation CDVBarcodeScanner
//--------------------------------------------------------------------------
- (NSString*)isScanNotPossible {
NSString* result = nil;
Class aClass = NSClassFromString(@"AVCaptureSession");
if (aClass == nil) {
return @"AVFoundation Framework not available";
}
return result;
}
-(BOOL)notHasPermission
{
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
return (authStatus == AVAuthorizationStatusDenied ||
authStatus == AVAuthorizationStatusRestricted);
}
-(BOOL)isUsageDescriptionSet
{
NSDictionary * plist = [[NSBundle mainBundle] infoDictionary];
if ([plist objectForKey:@"NSCameraUsageDescription" ] ||
[[NSBundle mainBundle] localizedStringForKey: @"NSCameraUsageDescription" value: nil table: @"InfoPlist"]) {
return YES;
}
return NO;
}
//--------------------------------------------------------------------------
- (void)scan:(CDVInvokedUrlCommand*)command {
CDVbcsProcessor* processor;
NSString* callback;
NSString* capabilityError;
callback = command.callbackId;
NSDictionary* options;
if (command.arguments.count == 0) {
options = [NSDictionary dictionary];
} else {
options = command.arguments[0];
}
BOOL preferFrontCamera = [options[@"preferFrontCamera"] boolValue];
BOOL showFlipCameraButton = [options[@"showFlipCameraButton"] boolValue];
BOOL showTorchButton = [options[@"showTorchButton"] boolValue];
BOOL disableAnimations = [options[@"disableAnimations"] boolValue];
BOOL disableSuccessBeep = [options[@"disableSuccessBeep"] boolValue];
// We allow the user to define an alternate xib file for loading the overlay.
NSString *overlayXib = options[@"overlayXib"];
capabilityError = [self isScanNotPossible];
if (capabilityError) {
[self returnError:capabilityError callback:callback];
return;
} else if ([self notHasPermission]) {
NSString * error = NSLocalizedString(@"Access to the camera has been prohibited; please enable it in the Settings app to continue.",nil);
[self returnError:error callback:callback];
return;
} else if (![self isUsageDescriptionSet]) {
NSString * error = NSLocalizedString(@"NSCameraUsageDescription is not set in the info.plist", nil);
[self returnError:error callback:callback];
return;
}
processor = [[CDVbcsProcessor alloc]
initWithPlugin:self
callback:callback
parentViewController:self.viewController
alterateOverlayXib:overlayXib
];
// queue [processor scanBarcode] to run on the event loop
if (preferFrontCamera) {
processor.isFrontCamera = true;
}
if (showFlipCameraButton) {
processor.isShowFlipCameraButton = true;
}
if (showTorchButton) {
processor.isShowTorchButton = true;
}
processor.isSuccessBeepEnabled = !disableSuccessBeep;
processor.isTransitionAnimated = !disableAnimations;
processor.formats = options[@"formats"];
[processor performSelector:@selector(scanBarcode) withObject:nil afterDelay:0];
}
//--------------------------------------------------------------------------
- (void)encode:(CDVInvokedUrlCommand*)command {
if([command.arguments count] < 1)
[self returnError:@"Too few arguments!" callback:command.callbackId];
CDVqrProcessor* processor;
NSString* callback;
callback = command.callbackId;
processor = [[CDVqrProcessor alloc]
initWithPlugin:self
callback:callback
stringToEncode: command.arguments[0][@"data"]
];
// queue [processor generateImage] to run on the event loop
[processor performSelector:@selector(generateImage) withObject:nil afterDelay:0];
}
- (void)returnImage:(NSString*)filePath format:(NSString*)format callback:(NSString*)callback{
NSMutableDictionary* resultDict = [[NSMutableDictionary alloc] init];
resultDict[@"format"] = format;
resultDict[@"file"] = filePath;
CDVPluginResult* result = [CDVPluginResult
resultWithStatus: CDVCommandStatus_OK
messageAsDictionary:resultDict
];
[[self commandDelegate] sendPluginResult:result callbackId:callback];
}
//--------------------------------------------------------------------------
- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback{
NSNumber* cancelledNumber = @(cancelled ? 1 : 0);
NSMutableDictionary* resultDict = [NSMutableDictionary new];
resultDict[@"text"] = scannedText;
resultDict[@"format"] = format;
resultDict[@"cancelled"] = cancelledNumber;
CDVPluginResult* result = [CDVPluginResult
resultWithStatus: CDVCommandStatus_OK
messageAsDictionary: resultDict
];
[self.commandDelegate sendPluginResult:result callbackId:callback];
}
//--------------------------------------------------------------------------
- (void)returnError:(NSString*)message callback:(NSString*)callback {
CDVPluginResult* result = [CDVPluginResult
resultWithStatus: CDVCommandStatus_ERROR
messageAsString: message
];
[self.commandDelegate sendPluginResult:result callbackId:callback];
}
@end
//------------------------------------------------------------------------------
// class that does the grunt work
//------------------------------------------------------------------------------
@implementation CDVbcsProcessor
@synthesize plugin = _plugin;
@synthesize callback = _callback;
@synthesize parentViewController = _parentViewController;
@synthesize viewController = _viewController;
@synthesize captureSession = _captureSession;
@synthesize previewLayer = _previewLayer;
@synthesize alternateXib = _alternateXib;
@synthesize is1D = _is1D;
@synthesize is2D = _is2D;
@synthesize capturing = _capturing;
@synthesize results = _results;
SystemSoundID _soundFileObject;
//--------------------------------------------------------------------------
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin
callback:(NSString*)callback
parentViewController:(UIViewController*)parentViewController
alterateOverlayXib:(NSString *)alternateXib {
self = [super init];
if (!self) return self;
self.plugin = plugin;
self.callback = callback;
self.parentViewController = parentViewController;
self.alternateXib = alternateXib;
self.is1D = YES;
self.is2D = YES;
self.capturing = NO;
self.results = [NSMutableArray new];
CFURLRef soundFileURLRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("CDVBarcodeScanner.bundle/beep"), CFSTR ("caf"), NULL);
AudioServicesCreateSystemSoundID(soundFileURLRef, &_soundFileObject);
return self;
}
//--------------------------------------------------------------------------
- (void)dealloc {
self.plugin = nil;
self.callback = nil;
self.parentViewController = nil;
self.viewController = nil;
self.captureSession = nil;
self.previewLayer = nil;
self.alternateXib = nil;
self.results = nil;
self.capturing = NO;
AudioServicesRemoveSystemSoundCompletion(_soundFileObject);
AudioServicesDisposeSystemSoundID(_soundFileObject);
}
//--------------------------------------------------------------------------
- (void)scanBarcode {
// self.captureSession = nil;
// self.previewLayer = nil;
NSString* errorMessage = [self setUpCaptureSession];
if (errorMessage) {
[self barcodeScanFailed:errorMessage];
return;
}
self.viewController = [[CDVbcsViewController alloc] initWithProcessor: self alternateOverlay:self.alternateXib];
// here we set the orientation delegate to the MainViewController of the app (orientation controlled in the Project Settings)
self.viewController.orientationDelegate = self.plugin.viewController;
// delayed [self openDialog];
[self performSelector:@selector(openDialog) withObject:nil afterDelay:1];
}
//--------------------------------------------------------------------------
- (void)openDialog {
[self.parentViewController
presentViewController:self.viewController
animated:self.isTransitionAnimated completion:nil
];
}
//--------------------------------------------------------------------------
- (void)barcodeScanDone:(void (^)(void))callbackBlock {
self.capturing = NO;
[self.captureSession stopRunning];
[self.parentViewController dismissViewControllerAnimated:self.isTransitionAnimated completion:callbackBlock];
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[device lockForConfiguration:nil];
if([device isAutoFocusRangeRestrictionSupported]) {
[device setAutoFocusRangeRestriction:AVCaptureAutoFocusRangeRestrictionNone];
}
[device unlockForConfiguration];
// viewcontroller holding onto a reference to us, release them so they
// will release us
self.viewController = nil;
}
//--------------------------------------------------------------------------
- (BOOL)checkResult:(NSString *)result {
[self.results addObject:result];
NSInteger treshold = 7;
if (self.results.count > treshold) {
[self.results removeObjectAtIndex:0];
}
if (self.results.count < treshold)
{
return NO;
}
BOOL allEqual = YES;
NSString *compareString = self.results[0];
for (NSString *aResult in self.results)
{
if (![compareString isEqualToString:aResult])
{
allEqual = NO;
//NSLog(@"Did not fit: %@",self.results);
break;
}
}
return allEqual;
}
//--------------------------------------------------------------------------
- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format {
dispatch_sync(dispatch_get_main_queue(), ^{
if (self.isSuccessBeepEnabled) {
AudioServicesPlaySystemSound(_soundFileObject);
}
[self barcodeScanDone:^{
[self.plugin returnSuccess:text format:format cancelled:FALSE flipped:FALSE callback:self.callback];
}];
});
}
//--------------------------------------------------------------------------
- (void)barcodeScanFailed:(NSString*)message {
dispatch_block_t block = ^{
[self barcodeScanDone:^{
[self.plugin returnError:message callback:self.callback];
}];
};
if ([NSThread isMainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
//--------------------------------------------------------------------------
- (void)barcodeScanCancelled {
[self barcodeScanDone:^{
[self.plugin returnSuccess:@"" format:@"" cancelled:TRUE flipped:self.isFlipped callback:self.callback];
}];
if (self.isFlipped) {
self.isFlipped = NO;
}
}
- (void)flipCamera {
self.isFlipped = YES;
self.isFrontCamera = !self.isFrontCamera;
[self barcodeScanDone:^{
if (self.isFlipped) {
self.isFlipped = NO;
}
[self performSelector:@selector(scanBarcode) withObject:nil afterDelay:0.1];
}];
}
- (void)toggleTorch {
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[device lockForConfiguration:nil];
if (device.flashActive) {
[device setTorchMode:AVCaptureTorchModeOff];
[device setFlashMode:AVCaptureFlashModeOff];
} else {
[device setTorchModeOnWithLevel:AVCaptureMaxAvailableTorchLevel error:nil];
[device setFlashMode:AVCaptureFlashModeOn];
}
[device unlockForConfiguration];
}
//--------------------------------------------------------------------------
- (NSString*)setUpCaptureSession {
NSError* error = nil;
AVCaptureSession* captureSession = [[AVCaptureSession alloc] init];
self.captureSession = captureSession;
AVCaptureDevice* __block device = nil;
if (self.isFrontCamera) {
NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
[devices enumerateObjectsUsingBlock:^(AVCaptureDevice *obj, NSUInteger idx, BOOL *stop) {
if (obj.position == AVCaptureDevicePositionFront) {
device = obj;
}
}];
} else {
device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (!device) return @"unable to obtain video capture device";
}
// set focus params if available to improve focusing
[device lockForConfiguration:&error];
if (error == nil) {
if([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
[device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
if([device isAutoFocusRangeRestrictionSupported]) {
[device setAutoFocusRangeRestriction:AVCaptureAutoFocusRangeRestrictionNear];
}
}
[device unlockForConfiguration];
AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) return @"unable to obtain video capture device input";
AVCaptureMetadataOutput* output = [[AVCaptureMetadataOutput alloc] init];
if (!output) return @"unable to obtain video capture output";
[output setMetadataObjectsDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)];
if ([captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
captureSession.sessionPreset = AVCaptureSessionPresetHigh;
} else if ([captureSession canSetSessionPreset:AVCaptureSessionPresetMedium]) {
captureSession.sessionPreset = AVCaptureSessionPresetMedium;
} else {
return @"unable to preset high nor medium quality video capture";
}
if ([captureSession canAddInput:input]) {
[captureSession addInput:input];
}
else {
return @"unable to add video capture device input to session";
}
if ([captureSession canAddOutput:output]) {
[captureSession addOutput:output];
}
else {
return @"unable to add video capture output to session";
}
[output setMetadataObjectTypes:[self formatObjectTypes]];
// setup capture preview layer
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
// run on next event loop pass [captureSession startRunning]
[captureSession performSelector:@selector(startRunning) withObject:nil afterDelay:0];
return nil;
}
//--------------------------------------------------------------------------
// this method gets sent the captured frames
//--------------------------------------------------------------------------
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection*)connection {
if (!self.capturing) return;
#if USE_SHUTTER
if (!self.viewController.shutterPressed) return;
self.viewController.shutterPressed = NO;
UIView* flashView = [[UIView alloc] initWithFrame:self.viewController.view.frame];
[flashView setBackgroundColor:[UIColor whiteColor]];
[self.viewController.view.window addSubview:flashView];
[UIView
animateWithDuration:.4f
animations:^{
[flashView setAlpha:0.f];
}
completion:^(BOOL finished){
[flashView removeFromSuperview];
}
];
#endif
try {
// This will bring in multiple entities if there are multiple 2D codes in frame.
for (AVMetadataObject *metaData in metadataObjects) {
AVMetadataMachineReadableCodeObject* code = (AVMetadataMachineReadableCodeObject*)[self.previewLayer transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject*)metaData];
if ([self checkResult:code.stringValue]) {
[self barcodeScanSucceeded:code.stringValue format:[self formatStringFromMetadata:code]];
}
}
}
catch (...) {
// NSLog(@"decoding: unknown exception");
// [self barcodeScanFailed:@"unknown exception decoding barcode"];
}
// NSTimeInterval timeElapsed = [NSDate timeIntervalSinceReferenceDate] - timeStart;
// NSLog(@"decoding completed in %dms", (int) (timeElapsed * 1000));
}
//--------------------------------------------------------------------------
// convert metadata object information to barcode format string
//--------------------------------------------------------------------------
- (NSString*)formatStringFromMetadata:(AVMetadataMachineReadableCodeObject*)format {
if (format.type == AVMetadataObjectTypeQRCode) return @"QR_CODE";
if (format.type == AVMetadataObjectTypeAztecCode) return @"AZTEC";
if (format.type == AVMetadataObjectTypeDataMatrixCode) return @"DATA_MATRIX";
if (format.type == AVMetadataObjectTypeUPCECode) return @"UPC_E";
// According to Apple documentation, UPC_A is EAN13 with a leading 0.
if (format.type == AVMetadataObjectTypeEAN13Code && [format.stringValue characterAtIndex:0] == '0') return @"UPC_A";
if (format.type == AVMetadataObjectTypeEAN8Code) return @"EAN_8";
if (format.type == AVMetadataObjectTypeEAN13Code) return @"EAN_13";
if (format.type == AVMetadataObjectTypeCode128Code) return @"CODE_128";
if (format.type == AVMetadataObjectTypeCode93Code) return @"CODE_93";
if (format.type == AVMetadataObjectTypeCode39Code) return @"CODE_39";
if (format.type == AVMetadataObjectTypeInterleaved2of5Code) return @"ITF";
if (format.type == AVMetadataObjectTypeITF14Code) return @"ITF_14";
if (format.type == AVMetadataObjectTypePDF417Code) return @"PDF_417";
return @"???";
}
//--------------------------------------------------------------------------
// convert string formats to metadata objects
//--------------------------------------------------------------------------
- (NSArray*) formatObjectTypes {
NSArray *supportedFormats = nil;
if (self.formats != nil) {
supportedFormats = [self.formats componentsSeparatedByString:@","];
}
NSMutableArray * formatObjectTypes = [NSMutableArray array];
if (self.formats == nil || [supportedFormats containsObject:@"QR_CODE"]) [formatObjectTypes addObject:AVMetadataObjectTypeQRCode];
if (self.formats == nil || [supportedFormats containsObject:@"AZTEC"]) [formatObjectTypes addObject:AVMetadataObjectTypeAztecCode];
if (self.formats == nil || [supportedFormats containsObject:@"DATA_MATRIX"]) [formatObjectTypes addObject:AVMetadataObjectTypeDataMatrixCode];
if (self.formats == nil || [supportedFormats containsObject:@"UPC_E"]) [formatObjectTypes addObject:AVMetadataObjectTypeUPCECode];
if (self.formats == nil || [supportedFormats containsObject:@"EAN_8"]) [formatObjectTypes addObject:AVMetadataObjectTypeEAN8Code];
if (self.formats == nil || [supportedFormats containsObject:@"EAN_13"]) [formatObjectTypes addObject:AVMetadataObjectTypeEAN13Code];
if (self.formats == nil || [supportedFormats containsObject:@"CODE_128"]) [formatObjectTypes addObject:AVMetadataObjectTypeCode128Code];
if (self.formats == nil || [supportedFormats containsObject:@"CODE_93"]) [formatObjectTypes addObject:AVMetadataObjectTypeCode93Code];
if (self.formats == nil || [supportedFormats containsObject:@"CODE_39"]) [formatObjectTypes addObject:AVMetadataObjectTypeCode39Code];
if (self.formats == nil || [supportedFormats containsObject:@"ITF"]) [formatObjectTypes addObject:AVMetadataObjectTypeInterleaved2of5Code];
if (self.formats == nil || [supportedFormats containsObject:@"ITF_14"]) [formatObjectTypes addObject:AVMetadataObjectTypeITF14Code];
if (self.formats == nil || [supportedFormats containsObject:@"PDF_417"]) [formatObjectTypes addObject:AVMetadataObjectTypePDF417Code];
return formatObjectTypes;
}
@end
//------------------------------------------------------------------------------
// qr encoder processor
//------------------------------------------------------------------------------
@implementation CDVqrProcessor
@synthesize plugin = _plugin;
@synthesize callback = _callback;
@synthesize stringToEncode = _stringToEncode;
@synthesize size = _size;
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback stringToEncode:(NSString*)stringToEncode{
self = [super init];
if (!self) return self;
self.plugin = plugin;
self.callback = callback;
self.stringToEncode = stringToEncode;
self.size = 300;
return self;
}
//--------------------------------------------------------------------------
- (void)dealloc {
self.plugin = nil;
self.callback = nil;
self.stringToEncode = nil;
}
//--------------------------------------------------------------------------
- (void)generateImage{
/* setup qr filter */
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setDefaults];
/* set filter's input message
* the encoding string has to be convert to a UTF-8 encoded NSData object */
[filter setValue:[self.stringToEncode dataUsingEncoding:NSUTF8StringEncoding]
forKey:@"inputMessage"];
/* on ios >= 7.0 set low image error correction level */
if (floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_7_0)
[filter setValue:@"L" forKey:@"inputCorrectionLevel"];
/* prepare cgImage */
CIImage *outputImage = [filter outputImage];
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef cgImage = [context createCGImage:outputImage
fromRect:[outputImage extent]];
/* returned qr code image */
UIImage *qrImage = [UIImage imageWithCGImage:cgImage
scale:1.
orientation:UIImageOrientationUp];
/* resize generated image */
CGFloat width = _size;
CGFloat height = _size;
UIGraphicsBeginImageContext(CGSizeMake(width, height));
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);
[qrImage drawInRect:CGRectMake(0, 0, width, height)];
qrImage = UIGraphicsGetImageFromCurrentImageContext();
/* clean up */
UIGraphicsEndImageContext();
CGImageRelease(cgImage);
/* save image to file */
NSString* fileName = [[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingString:@".jpg"];
NSString* filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
[UIImageJPEGRepresentation(qrImage, 1.0) writeToFile:filePath atomically:YES];
/* return file path back to cordova */
[self.plugin returnImage:filePath format:@"QR_CODE" callback: self.callback];
}
@end
//------------------------------------------------------------------------------
// view controller for the ui
//------------------------------------------------------------------------------
@implementation CDVbcsViewController
@synthesize processor = _processor;
@synthesize shutterPressed = _shutterPressed;
@synthesize alternateXib = _alternateXib;
@synthesize overlayView = _overlayView;
//--------------------------------------------------------------------------
- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib {
self = [super init];
if (!self) return self;
self.processor = processor;
self.shutterPressed = NO;
self.alternateXib = alternateXib;
self.overlayView = nil;
return self;
}
//--------------------------------------------------------------------------
- (void)dealloc {
self.view = nil;
self.processor = nil;
self.shutterPressed = NO;
self.alternateXib = nil;
self.overlayView = nil;
}
//--------------------------------------------------------------------------
- (void)loadView {
self.view = [[UIView alloc] initWithFrame: self.processor.parentViewController.view.frame];
}
//--------------------------------------------------------------------------
- (void)viewWillAppear:(BOOL)animated {
// set video orientation to what the camera sees
self.processor.previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
// this fixes the bug when the statusbar is landscape, and the preview layer
// starts up in portrait (not filling the whole view)
self.processor.previewLayer.frame = self.view.bounds;
}
//--------------------------------------------------------------------------
- (void)viewDidAppear:(BOOL)animated {
// setup capture preview layer
AVCaptureVideoPreviewLayer* previewLayer = self.processor.previewLayer;
previewLayer.frame = self.view.bounds;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
if ([previewLayer.connection isVideoOrientationSupported]) {
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
}
[self.view.layer insertSublayer:previewLayer below:[[self.view.layer sublayers] objectAtIndex:0]];
[self.view addSubview:[self buildOverlayView]];
[self startCapturing];
[super viewDidAppear:animated];
}
- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationPortrait:
return AVCaptureVideoOrientationPortrait;
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return AVCaptureVideoOrientationLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return AVCaptureVideoOrientationLandscapeRight;
default:
return AVCaptureVideoOrientationPortrait;
}
}
//--------------------------------------------------------------------------
- (void)startCapturing {
self.processor.capturing = YES;
}
//--------------------------------------------------------------------------
- (IBAction)shutterButtonPressed {
self.shutterPressed = YES;
}
//--------------------------------------------------------------------------
- (IBAction)cancelButtonPressed:(id)sender {
[self.processor performSelector:@selector(barcodeScanCancelled) withObject:nil afterDelay:0];
}
- (IBAction)flipCameraButtonPressed:(id)sender
{
[self.processor performSelector:@selector(flipCamera) withObject:nil afterDelay:0];
}
- (IBAction)torchButtonPressed:(id)sender
{
[self.processor performSelector:@selector(toggleTorch) withObject:nil afterDelay:0];
}
//--------------------------------------------------------------------------
- (UIView *)buildOverlayViewFromXib
{
[[NSBundle mainBundle] loadNibNamed:self.alternateXib owner:self options:NULL];
if ( self.overlayView == nil )
{
NSLog(@"%@", @"An error occurred loading the overlay xib. It appears that the overlayView outlet is not set.");
return nil;
}
self.overlayView.autoresizesSubviews = YES;
self.overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.overlayView.opaque = NO;
CGRect bounds = self.view.bounds;
bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
[self.overlayView setFrame:bounds];
return self.overlayView;
}
//--------------------------------------------------------------------------
- (UIView*)buildOverlayView {
if ( nil != self.alternateXib )
{
return [self buildOverlayViewFromXib];
}
CGRect bounds = self.view.frame;
bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
UIView* overlayView = [[UIView alloc] initWithFrame:bounds];
overlayView.autoresizesSubviews = YES;
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
overlayView.opaque = NO;
self.toolbar = [[UIToolbar alloc] init];
self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
id cancelButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:(id)self
action:@selector(cancelButtonPressed:)
];
id flexSpace = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil
];
id flipCamera = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
target:(id)self
action:@selector(flipCameraButtonPressed:)
];
NSMutableArray *items;
#if USE_SHUTTER
id shutterButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
target:(id)self
action:@selector(shutterButtonPressed)
];
if (_processor.isShowFlipCameraButton) {
items = [NSMutableArray arrayWithObjects:flexSpace, cancelButton, flexSpace, flipCamera, shutterButton, nil];
} else {
items = [NSMutableArray arrayWithObjects:flexSpace, cancelButton, flexSpace, shutterButton, nil];
}
#else
if (_processor.isShowFlipCameraButton) {
items = [@[flexSpace, cancelButton, flexSpace, flipCamera] mutableCopy];
} else {
items = [@[flexSpace, cancelButton, flexSpace] mutableCopy];
}
#endif
if (_processor.isShowTorchButton && !_processor.isFrontCamera) {
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device hasTorch] && [device hasFlash]) {
NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"CDVBarcodeScanner" withExtension:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
NSString *imagePath = [bundle pathForResource:@"torch" ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
id torchButton = [[UIBarButtonItem alloc]
initWithImage:image
style:UIBarButtonItemStylePlain
target:(id)self
action:@selector(torchButtonPressed:)
];
[items insertObject:torchButton atIndex:0];
}
}
self.toolbar.items = items;
[overlayView addSubview: self.toolbar];
UIImage* reticleImage = [self buildReticleImage];
self.reticleView = [[UIImageView alloc] initWithImage:reticleImage];
self.reticleView.opaque = NO;
self.reticleView.contentMode = UIViewContentModeScaleAspectFit;
self.reticleView.autoresizingMask = (UIViewAutoresizing) (0
| UIViewAutoresizingFlexibleLeftMargin
| UIViewAutoresizingFlexibleRightMargin
| UIViewAutoresizingFlexibleTopMargin
| UIViewAutoresizingFlexibleBottomMargin)
;
[overlayView addSubview: self.reticleView];
[self resizeElements];
return overlayView;
}
//--------------------------------------------------------------------------
#define RETICLE_SIZE 500.0f
#define RETICLE_WIDTH 10.0f
#define RETICLE_OFFSET 60.0f
#define RETICLE_ALPHA 0.4f
//-------------------------------------------------------------------------
// builds the green box and red line
//-------------------------------------------------------------------------
- (UIImage*)buildReticleImage {
UIImage* result;
UIGraphicsBeginImageContext(CGSizeMake(RETICLE_SIZE, RETICLE_SIZE));
CGContextRef context = UIGraphicsGetCurrentContext();
if (self.processor.is1D) {
UIColor* color = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:RETICLE_ALPHA];
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGContextSetLineWidth(context, RETICLE_WIDTH);
CGContextBeginPath(context);
CGFloat lineOffset = (CGFloat) (RETICLE_OFFSET+(0.5*RETICLE_WIDTH));
CGContextMoveToPoint(context, lineOffset, RETICLE_SIZE/2);
CGContextAddLineToPoint(context, RETICLE_SIZE-lineOffset, (CGFloat) (0.5*RETICLE_SIZE));
CGContextStrokePath(context);
}
if (self.processor.is2D) {
UIColor* color = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:RETICLE_ALPHA];
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGContextSetLineWidth(context, RETICLE_WIDTH);
CGContextStrokeRect(context,
CGRectMake(
RETICLE_OFFSET,
RETICLE_OFFSET,
RETICLE_SIZE-2*RETICLE_OFFSET,
RETICLE_SIZE-2*RETICLE_OFFSET
)
);
}
result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
#pragma mark CDVBarcodeScannerOrientationDelegate
- (BOOL)shouldAutorotate
{
return YES;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[UIApplication sharedApplication] statusBarOrientation];
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
return YES;
}
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
[UIView setAnimationsEnabled:NO];
AVCaptureVideoPreviewLayer* previewLayer = self.processor.previewLayer;
previewLayer.frame = self.view.bounds;
if (orientation == UIInterfaceOrientationLandscapeLeft) {
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft];
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeRight];
} else if (orientation == UIInterfaceOrientationPortrait) {
[previewLayer setOrientation:AVCaptureVideoOrientationPortrait];
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
[previewLayer setOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
}
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self resizeElements];
[UIView setAnimationsEnabled:YES];
}
-(void) resizeElements {
CGRect bounds = self.view.bounds;
if (@available(iOS 11.0, *)) {
bounds = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.width, self.view.safeAreaLayoutGuide.layoutFrame.size.height+self.view.safeAreaLayoutGuide.layoutFrame.origin.y);
}
[self.toolbar sizeToFit];
CGFloat toolbarHeight = [self.toolbar frame].size.height;
CGFloat rootViewHeight = CGRectGetHeight(bounds);
CGFloat rootViewWidth = CGRectGetWidth(bounds);
CGRect rectArea = CGRectMake(0, rootViewHeight - toolbarHeight, rootViewWidth, toolbarHeight);
[self.toolbar setFrame:rectArea];
CGFloat minAxis = MIN(rootViewHeight, rootViewWidth);
rectArea = CGRectMake(
(CGFloat) (0.5 * (rootViewWidth - minAxis)),
(CGFloat) (0.5 * (rootViewHeight - minAxis)),
minAxis,
minAxis
);
[self.reticleView setFrame:rectArea];
self.reticleView.center = CGPointMake(self.view.center.x, self.view.center.y-self.toolbar.frame.size.height/2);
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C74</string>
<string key="IBDocument.InterfaceBuilderVersion">1938</string>
<string key="IBDocument.AppKitVersion">1138.23</string>
<string key="IBDocument.HIToolboxVersion">567.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">933</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBUINavigationItem</string>
<string>IBUIBarButtonItem</string>
<string>IBUIView</string>
<string>IBUINavigationBar</string>
<string>IBProxyObject</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="191373211">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUINavigationBar" id="1064216609">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">290</int>
<string key="NSFrameSize">{320, 44}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:260</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIBarStyle">1</int>
<array class="NSMutableArray" key="IBUIItems">
<object class="IBUINavigationItem" id="240626599">
<reference key="IBUINavigationBar" ref="1064216609"/>
<string key="IBUITitle">Barcode Scanner</string>
<object class="IBUIBarButtonItem" key="IBUILeftBarButtonItem" id="1053701234">
<string key="IBUITitle">Cancel</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIStyle">1</int>
<reference key="IBUINavigationItem" ref="240626599"/>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</array>
</object>
</array>
<string key="NSFrameSize">{320, 460}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="1064216609"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MSAwAA</bytes>
<object class="NSColorSpace" key="NSCustomColorSpace">
<int key="NSID">2</int>
</object>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">overlayView</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="191373211"/>
</object>
<int key="connectionID">9</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="191373211"/>
<array class="NSMutableArray" key="children">
<reference ref="1064216609"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">3</int>
<reference key="object" ref="1064216609"/>
<array class="NSMutableArray" key="children">
<reference ref="240626599"/>
</array>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="240626599"/>
<array class="NSMutableArray" key="children">
<reference ref="1053701234"/>
</array>
<reference key="parent" ref="1064216609"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">10</int>
<reference key="object" ref="1053701234"/>
<reference key="parent" ref="240626599"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">PGbcsViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="10.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="3.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="4.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">11</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">PGbcsViewController</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">overlayView</string>
<string key="NS.object.0">UIView</string>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<string key="NS.key.0">overlayView</string>
<object class="IBToOneOutletInfo" key="NS.object.0">
<string key="name">overlayView</string>
<string key="candidateClassName">UIView</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/PGbcsViewController.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">933</string>
</data>
</archive>
/*
* Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
var urlutil = require('cordova/urlutil');
var CAMERA_STREAM_STATE_CHECK_RETRY_TIMEOUT = 200; // milliseconds
var OPERATION_IS_IN_PROGRESS = -2147024567;
var REGDB_E_CLASSNOTREG = -2147221164;
var INITIAL_FOCUS_DELAY = 200; // milliseconds
var CHECK_PLAYING_TIMEOUT = 100; // milliseconds
/**
* List of supported barcode formats from ZXing library. Used to return format
* name instead of number code as per plugin spec.
*
* @enum {String}
*/
var BARCODE_FORMAT = {
1: 'AZTEC',
2: 'CODABAR',
4: 'CODE_39',
8: 'CODE_93',
16: 'CODE_128',
32: 'DATA_MATRIX',
64: 'EAN_8',
128: 'EAN_13',
256: 'ITF',
512: 'MAXICODE',
1024: 'PDF_417',
2048: 'QR_CODE',
4096: 'RSS_14',
8192: 'RSS_EXPANDED',
16384: 'UPC_A',
32768: 'UPC_E',
61918: 'All_1D',
65536: 'UPC_EAN_EXTENSION',
131072: 'MSI',
262144: 'PLESSEY'
};
/**
* Detects the first appropriate camera located at the back panel of device. If
* there is no back cameras, returns the first available.
*
* @returns {Promise<String>} Camera id
*/
function findCamera() {
var Devices = Windows.Devices.Enumeration;
// Enumerate cameras and add them to the list
return Devices.DeviceInformation.findAllAsync(Devices.DeviceClass.videoCapture)
.then(function (cameras) {
if (!cameras || cameras.length === 0) {
throw new Error("No cameras found");
}
var backCameras = cameras.filter(function (camera) {
return camera.enclosureLocation && camera.enclosureLocation.panel === Devices.Panel.back;
});
// If there is back cameras, return the id of the first,
// otherwise take the first available device's id
return (backCameras[0] || cameras[0]).id;
});
}
/**
* @param {Windows.Graphics.Display.DisplayOrientations} displayOrientation
* @return {Number}
*/
function videoPreviewRotationLookup(displayOrientation, isMirrored) {
var degreesToRotate;
switch (displayOrientation) {
case Windows.Graphics.Display.DisplayOrientations.landscape:
degreesToRotate = 0;
break;
case Windows.Graphics.Display.DisplayOrientations.portrait:
if (isMirrored) {
degreesToRotate = 270;
} else {
degreesToRotate = 90;
}
break;
case Windows.Graphics.Display.DisplayOrientations.landscapeFlipped:
degreesToRotate = 180;
break;
case Windows.Graphics.Display.DisplayOrientations.portraitFlipped:
if (isMirrored) {
degreesToRotate = 90;
} else {
degreesToRotate = 270;
}
break;
default:
degreesToRotate = 0;
break;
}
return degreesToRotate;
}
/**
* The pure JS implementation of barcode reader from WinRTBarcodeReader.winmd.
* Works only on Windows 10 devices and more efficient than original one.
*
* @class {BarcodeReader}
*/
function BarcodeReader () {
this._promise = null;
this._cancelled = false;
}
/**
* Returns an instance of Barcode reader, depending on capabilities of Media
* Capture API
*
* @static
* @constructs {BarcodeReader}
*
* @param {MediaCapture} mediaCaptureInstance Instance of
* Windows.Media.Capture.MediaCapture class
*
* @return {BarcodeReader} BarcodeReader instance that could be used for
* scanning
*/
BarcodeReader.get = function (mediaCaptureInstance) {
if (mediaCaptureInstance.getPreviewFrameAsync && ZXing.BarcodeReader) {
return new BarcodeReader();
}
// If there is no corresponding API (Win8/8.1/Phone8.1) use old approach with WinMD library
return new WinRTBarcodeReader.Reader();
};
/**
* Initializes instance of reader.
*
* @param {MediaCapture} capture Instance of
* Windows.Media.Capture.MediaCapture class, used for acquiring images/ video
* stream for barcode scanner.
* @param {Number} width Video/image frame width
* @param {Number} height Video/image frame height
*/
BarcodeReader.prototype.init = function (capture, width, height) {
this._capture = capture;
this._width = width;
this._height = height;
this._zxingReader = new ZXing.BarcodeReader();
this._zxingReader.tryHarder = true;
var formatsList = BarcodeReader.scanCallArgs.args.length > 0 && BarcodeReader.scanCallArgs.args[0].formats;
if (formatsList) {
var possibleFormats = formatsList
.split(",")
.map(format => {
for (var index in BARCODE_FORMAT) {
if (BARCODE_FORMAT[index] === format) {
return index;
}
}
});
this._zxingReader.possibleFormats = possibleFormats;
}
};
/**
* Starts barcode search routines asyncronously.
*
* @return {Promise<ScanResult>} barcode scan result or null if search
* cancelled.
*/
BarcodeReader.prototype.readCode = function () {
/**
* Grabs a frame from preview stream uning Win10-only API and tries to
* get a barcode using zxing reader provided. If there is no barcode
* found, returns null.
*/
function scanBarcodeAsync(mediaCapture, zxingReader, frameWidth, frameHeight) {
// Shortcuts for namespaces
var Imaging = Windows.Graphics.Imaging;
var Streams = Windows.Storage.Streams;
var frame = new Windows.Media.VideoFrame(Imaging.BitmapPixelFormat.bgra8, frameWidth, frameHeight);
return mediaCapture.getPreviewFrameAsync(frame)
.then(function (capturedFrame) {
// Copy captured frame to buffer for further deserialization
var bitmap = capturedFrame.softwareBitmap;
var rawBuffer = new Streams.Buffer(bitmap.pixelWidth * bitmap.pixelHeight * 4);
capturedFrame.softwareBitmap.copyToBuffer(rawBuffer);
capturedFrame.close();
// Get raw pixel data from buffer
var data = new Uint8Array(rawBuffer.length);
var dataReader = Streams.DataReader.fromBuffer(rawBuffer);
dataReader.readBytes(data);
dataReader.close();
return zxingReader.decode(data, frameWidth, frameHeight, ZXing.BitmapFormat.bgra32);
});
}
var self = this;
return scanBarcodeAsync(this._capture, this._zxingReader, this._width, this._height)
.then(function (result) {
if (self._cancelled)
return null;
return result || (self._promise = self.readCode());
});
};
/**
* Stops barcode search
*/
BarcodeReader.prototype.stop = function () {
this._cancelled = true;
};
function degreesToRotation(degrees) {
switch (degrees) {
// portrait
case 90:
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
// landscape
case 0:
return Windows.Media.Capture.VideoRotation.none;
// portrait-flipped
case 270:
return Windows.Media.Capture.VideoRotation.clockwise270Degrees;
// landscape-flipped
case 180:
return Windows.Media.Capture.VideoRotation.clockwise180Degrees;
default:
// Falling back to portrait default
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
}
}
module.exports = {
/**
* Scans image via device camera and retieves barcode from it.
* @param {function} success Success callback
* @param {function} fail Error callback
* @param {array} args Arguments array
*/
scan: function (success, fail, args) {
var capturePreview,
capturePreviewAlignmentMark,
captureCancelButton,
navigationButtonsDiv,
previewMirroring,
closeButton,
capture,
reader;
// Save call state for suspend/resume
BarcodeReader.scanCallArgs = {
success: success,
fail: fail,
args: args
};
function updatePreviewForRotation(evt) {
if (!capture) {
return;
}
var displayInformation = (evt && evt.target) || Windows.Graphics.Display.DisplayInformation.getForCurrentView();
var currentOrientation = displayInformation.currentOrientation;
previewMirroring = capture.getPreviewMirroring();
// Lookup up the rotation degrees.
var rotDegree = videoPreviewRotationLookup(currentOrientation, previewMirroring);
capture.setPreviewRotation(degreesToRotation(rotDegree));
return WinJS.Promise.as();
}
/**
* Creates a preview frame and necessary objects
*/
function createPreview() {
// Create fullscreen preview
var capturePreviewFrameStyle = document.createElement('link');
capturePreviewFrameStyle.rel = "stylesheet";
capturePreviewFrameStyle.type = "text/css";
capturePreviewFrameStyle.href = urlutil.makeAbsolute("/www/css/plugin-barcodeScanner.css");
document.head.appendChild(capturePreviewFrameStyle);
capturePreviewFrame = document.createElement('div');
capturePreviewFrame.className = "barcode-scanner-wrap";
capturePreview = document.createElement("video");
capturePreview.className = "barcode-scanner-preview";
capturePreview.addEventListener('click', function () {
focus();
});
capturePreviewAlignmentMark = document.createElement('div');
capturePreviewAlignmentMark.className = "barcode-scanner-mark";
navigationButtonsDiv = document.createElement("div");
navigationButtonsDiv.className = "barcode-scanner-app-bar";
navigationButtonsDiv.onclick = function (e) {
e.cancelBubble = true;
};
closeButton = document.createElement("div");
closeButton.innerText = "close";
closeButton.className = "app-bar-action action-close";
navigationButtonsDiv.appendChild(closeButton);
BarcodeReader.scanCancelled = false;
closeButton.addEventListener("click", cancelPreview, false);
document.addEventListener('backbutton', cancelPreview, false);
[capturePreview, capturePreviewAlignmentMark, navigationButtonsDiv].forEach(function (element) {
capturePreviewFrame.appendChild(element);
});
}
function focus(controller) {
var result = WinJS.Promise.wrap();
if (!capturePreview || capturePreview.paused) {
// If the preview is not yet playing, there is no sense in running focus
return result;
}
if (!controller) {
try {
controller = capture && capture.videoDeviceController;
} catch (err) {
console.log('Failed to access focus control for current camera: ' + err);
return result;
}
}
if (!controller.focusControl || !controller.focusControl.supported) {
console.log('Focus control for current camera is not supported');
return result;
}
// Multiple calls to focusAsync leads to internal focusing hang on some Windows Phone 8.1 devices
// Also need to wrap in try/catch to avoid crash on Surface 3 - looks like focusState property
// somehow is not accessible there. See https://github.com/phonegap/phonegap-plugin-barcodescanner/issues/288
try {
if (controller.focusControl.focusState === Windows.Media.Devices.MediaCaptureFocusState.searching) {
return result;
}
} catch (e) {
// Nothing to do - just continue w/ focusing
}
// The delay prevents focus hang on slow devices
return WinJS.Promise.timeout(INITIAL_FOCUS_DELAY)
.then(function () {
try {
return controller.focusControl.focusAsync().then(function () {
return result;
}, function (e) {
// This happens on mutliple taps
if (e.number !== OPERATION_IS_IN_PROGRESS) {
console.error('focusAsync failed: ' + e);
return WinJS.Promise.wrapError(e);
}
return result;
});
} catch (e) {
// This happens on mutliple taps
if (e.number !== OPERATION_IS_IN_PROGRESS) {
console.error('focusAsync failed: ' + e);
return WinJS.Promise.wrapError(e);
}
return result;
}
});
}
function setupFocus(focusControl) {
function supportsFocusMode(mode) {
return focusControl.supportedFocusModes.indexOf(mode).returnValue;
}
if (!focusControl || !focusControl.supported || !focusControl.configure) {
return WinJS.Promise.wrap();
}
var FocusMode = Windows.Media.Devices.FocusMode;
var focusConfig = new Windows.Media.Devices.FocusSettings();
focusConfig.autoFocusRange = Windows.Media.Devices.AutoFocusRange.normal;
// Determine a focus position if the focus search fails:
focusConfig.disableDriverFallback = false;
if (supportsFocusMode(FocusMode.continuous)) {
console.log("Device supports continuous focus mode");
focusConfig.mode = FocusMode.continuous;
} else if (supportsFocusMode(FocusMode.auto)) {
console.log("Device doesn\'t support continuous focus mode, switching to autofocus mode");
focusConfig.mode = FocusMode.auto;
}
focusControl.configure(focusConfig);
// Continuous focus should start only after preview has started. See 'Remarks' at
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.devices.focuscontrol.configure.aspx
function waitForIsPlaying() {
var isPlaying = !capturePreview.paused && !capturePreview.ended && capturePreview.readyState > 2;
if (!isPlaying) {
return WinJS.Promise.timeout(CHECK_PLAYING_TIMEOUT)
.then(function () {
return waitForIsPlaying();
});
}
return focus();
}
return waitForIsPlaying();
}
function disableZoomAndScroll() {
document.body.classList.add('no-zoom');
document.body.classList.add('no-scroll');
}
function enableZoomAndScroll() {
document.body.classList.remove('no-zoom');
document.body.classList.remove('no-scroll');
}
/**
* Starts stream transmission to preview frame and then run barcode search
*/
function startPreview() {
return findCamera()
.then(function (id) {
var captureSettings;
try {
captureSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
} catch (e) {
if (e.number === REGDB_E_CLASSNOTREG) {
throw new Error('Ensure that you have Windows Media Player and Media Feature pack installed.');
}
throw e;
}
captureSettings.streamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.video;
captureSettings.photoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.videoPreview;
captureSettings.videoDeviceId = id;
capture = new Windows.Media.Capture.MediaCapture();
return capture.initializeAsync(captureSettings);
})
.then(function () {
var controller = capture.videoDeviceController;
var deviceProps = controller.getAvailableMediaStreamProperties(Windows.Media.Capture.MediaStreamType.videoPreview);
deviceProps = Array.prototype.slice.call(deviceProps);
deviceProps = deviceProps.filter(function (prop) {
// filter out streams with "unknown" subtype - causes errors on some devices
return prop.subtype !== "Unknown";
}).sort(function (propA, propB) {
// sort properties by resolution
return propB.width - propA.width;
});
var preferredProps = deviceProps.filter(function(prop){
// Filter out props where frame size is between 640*480 and 1280*720
return prop.width >= 640 && prop.height >= 480 && prop.width <= 1280 && prop.height <= 720;
});
// prefer video frame size between between 640*480 and 1280*720
// use maximum resolution otherwise
var maxResProps = preferredProps[0] || deviceProps[0];
return controller.setMediaStreamPropertiesAsync(Windows.Media.Capture.MediaStreamType.videoPreview, maxResProps)
.then(function () {
return {
capture: capture,
width: maxResProps.width,
height: maxResProps.height
};
});
})
.then(function (captureSettings) {
capturePreview.msZoom = true;
capturePreview.src = URL.createObjectURL(capture);
capturePreview.play();
// Insert preview frame and controls into page
document.body.appendChild(capturePreviewFrame);
disableZoomAndScroll();
return setupFocus(captureSettings.capture.videoDeviceController.focusControl)
.then(function () {
Windows.Graphics.Display.DisplayInformation.getForCurrentView().addEventListener("orientationchanged", updatePreviewForRotation, false);
return updatePreviewForRotation();
})
.then(function () {
if (!Windows.Media.Devices.CameraStreamState) {
// CameraStreamState is available starting with Windows 10 so skip this check for 8.1
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.devices.camerastreamstate
return WinJS.Promise.as();
}
function checkCameraStreamState() {
if (capture.cameraStreamState !== Windows.Media.Devices.CameraStreamState.streaming) {
// Using loop as MediaCapture.CameraStreamStateChanged does not fire with CameraStreamState.streaming state.
return WinJS.Promise.timeout(CAMERA_STREAM_STATE_CHECK_RETRY_TIMEOUT)
.then(function () {
return checkCameraStreamState();
});
}
return WinJS.Promise.as();
}
// Ensure CameraStreamState is Streaming
return checkCameraStreamState();
})
.then(function () {
return captureSettings;
});
});
}
/**
* Removes preview frame and corresponding objects from window
*/
function destroyPreview() {
var promise = WinJS.Promise.as();
Windows.Graphics.Display.DisplayInformation.getForCurrentView().removeEventListener("orientationchanged", updatePreviewForRotation, false);
document.removeEventListener('backbutton', cancelPreview);
if (capturePreview) {
var isPlaying = !capturePreview.paused && !capturePreview.ended && capturePreview.readyState > 2;
if (isPlaying) {
capturePreview.pause();
}
// http://stackoverflow.com/a/28060352/4177762
capturePreview.src = "";
if (capturePreview.load) {
capturePreview.load();
}
}
if (capturePreviewFrame) {
try {
document.body.removeChild(capturePreviewFrame);
} catch (e) {
// Catching NotFoundError
console.error(e);
}
}
capturePreviewFrame = null;
reader && reader.stop();
reader = null;
if (capture) {
try {
promise = capture.stopRecordAsync();
} catch (e) {
// Catching NotFoundError
console.error(e);
}
}
capture = null;
enableZoomAndScroll();
return promise;
}
/**
* Stops preview and then call success callback with cancelled=true
* See https://github.com/phonegap-build/BarcodeScanner#using-the-plugin
*/
function cancelPreview() {
BarcodeReader.scanCancelled = true;
reader && reader.stop();
}
function checkCancelled() {
if (BarcodeReader.scanCancelled || BarcodeReader.suspended) {
throw new Error('Canceled');
}
}
// Timeout is needed so that the .done finalizer below can be attached to the promise.
BarcodeReader.scanPromise = WinJS.Promise.timeout()
.then(function() {
createPreview();
checkCancelled();
return startPreview();
})
.then(function (captureSettings) {
checkCancelled();
reader = BarcodeReader.get(captureSettings.capture);
reader.init(captureSettings.capture, captureSettings.width, captureSettings.height);
// Add a small timeout before capturing first frame otherwise
// we would get an 'Invalid state' error from 'getPreviewFrameAsync'
return WinJS.Promise.timeout(200)
.then(function () {
checkCancelled();
return reader.readCode();
});
})
.then(function (result) {
// Suppress null result (cancel) on suspending
if (BarcodeReader.suspended) {
return;
}
destroyPreview();
success({
text: result && result.text,
format: result && BARCODE_FORMAT[result.barcodeFormat],
cancelled: !result
});
});
// Catching any errors here
BarcodeReader.scanPromise.done(function () { }, function (error) {
// Suppress null result (cancel) on suspending
if (BarcodeReader.suspended) {
return;
}
destroyPreview();
if (error.message == 'Canceled') {
success({
cancelled: true
});
} else {
fail(error);
}
});
BarcodeReader.videoPreviewIsVisible = function () {
return capturePreviewFrame !== null;
}
BarcodeReader.destroyPreview = destroyPreview;
},
/**
* Encodes specified data into barcode
* @param {function} success Success callback
* @param {function} fail Error callback
* @param {array} args Arguments array
*/
encode: function (success, fail, args) {
fail("Not implemented yet");
}
};
var app = WinJS.Application;
function waitForScanEnd() {
return BarcodeReader.scanPromise || WinJS.Promise.as();
}
function suspend(args) {
BarcodeReader.suspended = true;
if (args) {
args.setPromise(BarcodeReader.destroyPreview()
.then(waitForScanEnd, waitForScanEnd));
} else {
BarcodeReader.destroyPreview();
}
}
function resume() {
BarcodeReader.suspended = false;
module.exports.scan(BarcodeReader.scanCallArgs.success, BarcodeReader.scanCallArgs.fail, BarcodeReader.scanCallArgs.args);
}
function onVisibilityChanged() {
if (document.visibilityState === 'hidden'
&& BarcodeReader.videoPreviewIsVisible && BarcodeReader.videoPreviewIsVisible() && BarcodeReader.destroyPreview) {
suspend();
} else if (BarcodeReader.suspended) {
resume();
}
}
// Windows 8.1 projects
document.addEventListener('msvisibilitychange', onVisibilityChanged);
// Windows 10 projects
document.addEventListener('visibilitychange', onVisibilityChanged);
// About to be suspended
app.addEventListener('checkpoint', function (args) {
if (BarcodeReader.videoPreviewIsVisible && BarcodeReader.videoPreviewIsVisible() && BarcodeReader.destroyPreview) {
suspend(args);
}
});
// Resuming from a user suspension
Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function () {
if (BarcodeReader.suspended) {
resume();
}
}, false);
require("cordova/exec/proxy").add("BarcodeScanner", module.exports);
.barcode-scanner-wrap {
margin: 0;
padding: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: 0 0 black;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999999;
-ms-user-select: none;
}
.barcode-scanner-preview {
width: auto;
height: calc(100% - 70px);
position: absolute;
top: calc(50% - 35px);
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
.barcode-scanner-mark {
position: absolute;
left: 0;
top: 50%;
width: 100%;
height: 3px;
background: red;
z-index: 9999999;
}
.barcode-scanner-app-bar {
height: 70px;
width: 100%;
padding-top: 10px;
z-index: 9999999;
text-align: center;
user-select: none;
position: absolute;
bottom: 0px;
}
.app-bar-action {
width: 40px;
height: 40px;
margin: 0 auto;
font-family: "Segoe UI Symbol";
color: white;
font-size: 12px;
text-transform: lowercase;
text-align: center;
cursor: default;
}
@media all and (orientation: landscape) {
.app-bar-action {
float: right;
margin-right: 20px;
}
}
.app-bar-action::before {
font-size: 28px;
display: block;
height: 36px;
}
.action-close::before {
content: "\E0C7";
/* close icon is larger so we re-size it to fit other icons */
font-size: 20px;
line-height: 40px;
}
.action-close:hover::before {
content: "\E0CA";
}
.no-zoom {
-ms-content-zooming: none;
}
.no-scroll {
overflow: hidden;
}
/*
* Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WinRTBarcodeReader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WinRTBarcodeReader")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
\ No newline at end of file
/*
* Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
namespace WinRTBarcodeReader
{
using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Media.Capture;
using Windows.Media.MediaProperties;
using Windows.Storage.Streams;
using ZXing;
/// <summary>
/// Defines the Reader type, that perform barcode search asynchronously.
/// </summary>
public sealed class Reader
{
#region Private fields
/// <summary>
/// Data reader, used to create bitmap array.
/// </summary>
private BarcodeReader barcodeReader;
/// <summary>
/// The cancel search flag.
/// </summary>
private CancellationTokenSource cancelSearch;
/// <summary>
/// MediaCapture instance, used for barcode search.
/// </summary>
private MediaCapture capture;
/// <summary>
/// Encoding properties for mediaCapture object.
/// </summary>
private ImageEncodingProperties encodingProps;
/// <summary>
/// Image stream for MediaCapture content.
/// </summary>
private InMemoryRandomAccessStream imageStream;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="Reader" /> class.
/// </summary>
/// <param name="capture">MediaCapture instance.</param>
/// <param name="width">Capture frame width.</param>
/// <param name="height">Capture frame height.</param>
public void Init(MediaCapture capture, uint width, uint height)
{
this.capture = capture;
encodingProps = ImageEncodingProperties.CreateJpeg();
encodingProps.Width = width;
encodingProps.Height = height;
barcodeReader = new BarcodeReader {Options = {TryHarder = true}};
cancelSearch = new CancellationTokenSource();
}
#endregion
#region Public methods
/// <summary>
/// Perform async MediaCapture analysis and searches for barcode.
/// </summary>
/// <returns>IAsyncOperation object</returns>
public IAsyncOperation<Result> ReadCode()
{
return this.Read().AsAsyncOperation();
}
/// <summary>
/// Send signal to stop barcode search.
/// </summary>
public void Stop()
{
this.cancelSearch.Cancel();
}
#endregion
#region Private methods
/// <summary>
/// Perform async MediaCapture analysis and searches for barcode.
/// </summary>
/// <returns>Task object</returns>
private async Task<Result> Read()
{
Result result = null;
try
{
while (result == null)
{
result = await GetCameraImage(cancelSearch.Token);
}
}
catch (OperationCanceledException) {
Console.WriteLine("Barcode reading operation was canceled.");
}
return result;
}
/// <summary>
/// Perform image capture from mediaCapture object
/// </summary>
/// <param name="cancelToken">
/// The cancel Token.
/// </param>
/// <returns>
/// Decoded barcode string.
/// </returns>
private async Task<Result> GetCameraImage(CancellationToken cancelToken)
{
if (cancelToken.IsCancellationRequested)
{
throw new OperationCanceledException(cancelToken);
}
imageStream = new InMemoryRandomAccessStream();
await capture.CapturePhotoToStreamAsync(encodingProps, imageStream);
await imageStream.FlushAsync();
var decoder = await BitmapDecoder.CreateAsync(imageStream);
byte[] pixels =
(await
decoder.GetPixelDataAsync(BitmapPixelFormat.Rgba8,
BitmapAlphaMode.Ignore,
new BitmapTransform(),
ExifOrientationMode.IgnoreExifOrientation,
ColorManagementMode.DoNotColorManage)).DetachPixelData();
const BitmapFormat format = BitmapFormat.RGB32;
imageStream.Dispose();
var result =
await
Task.Run(
() => barcodeReader.Decode(pixels, (int) decoder.PixelWidth, (int) decoder.PixelHeight, format),
cancelToken);
return result;
}
#endregion
}
}
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-->
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectName>WinRTBarcodeReader</ProjectName>
<ProjectGuid>{01412F36-3781-4AF0-903C-ACEA7552C99C}</ProjectGuid>
<OutputType>winmdobj</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WinRTBarcodeReader</RootNamespace>
<AssemblyName>WinRTBarcodeReader</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetPlatformVersion>8.1</TargetPlatformVersion>
<MinimumVisualStudioVersion>12</MinimumVisualStudioVersion>
<TargetFrameworkVersion />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Compile Include="Reader.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="ZXing">
<HintPath>ZXing.winmd</HintPath>
</Reference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' ">
<VisualStudioVersion>12.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
/**
* cordova is available under the MIT License (2008).
* See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) Matt Kane 2010
* Copyright (c) 2011, IBM Corporation
* Copyright (c) 2012-2017, Adobe Systems
*/
var exec = cordova.require("cordova/exec");
var scanInProgress = false;
/**
* Constructor.
*
* @returns {BarcodeScanner}
*/
function BarcodeScanner() {
/**
* Encoding constants.
*
* @type Object
*/
this.Encode = {
TEXT_TYPE: "TEXT_TYPE",
EMAIL_TYPE: "EMAIL_TYPE",
PHONE_TYPE: "PHONE_TYPE",
SMS_TYPE: "SMS_TYPE"
// CONTACT_TYPE: "CONTACT_TYPE", // TODO: not implemented, requires passing a Bundle class from Javascript to Java
// LOCATION_TYPE: "LOCATION_TYPE" // TODO: not implemented, requires passing a Bundle class from Javascript to Java
};
/**
* Barcode format constants, defined in ZXing library.
*
* @type Object
*/
this.format = {
"all_1D": 61918,
"aztec": 1,
"codabar": 2,
"code_128": 16,
"code_39": 4,
"code_93": 8,
"data_MATRIX": 32,
"ean_13": 128,
"ean_8": 64,
"itf": 256,
"maxicode": 512,
"msi": 131072,
"pdf_417": 1024,
"plessey": 262144,
"qr_CODE": 2048,
"rss_14": 4096,
"rss_EXPANDED": 8192,
"upc_A": 16384,
"upc_E": 32768,
"upc_EAN_EXTENSION": 65536
};
}
/**
* Read code from scanner.
*
* @param {Function} successCallback This function will recieve a result object: {
* text : '12345-mock', // The code that was scanned.
* format : 'FORMAT_NAME', // Code format.
* cancelled : true/false, // Was canceled.
* }
* @param {Function} errorCallback
* @param config
*/
BarcodeScanner.prototype.scan = function (successCallback, errorCallback, config) {
if (config instanceof Array) {
// do nothing
} else {
if (typeof(config) === 'object') {
// string spaces between formats, ZXing does not like that
if (config.formats) {
config.formats = config.formats.replace(/\s+/g, '');
}
config = [ config ];
} else {
config = [];
}
}
if (errorCallback == null) {
errorCallback = function () {
};
}
if (typeof errorCallback != "function") {
console.log("BarcodeScanner.scan failure: failure parameter not a function");
return;
}
if (typeof successCallback != "function") {
console.log("BarcodeScanner.scan failure: success callback parameter must be a function");
return;
}
if (scanInProgress) {
errorCallback('Scan is already in progress');
return;
}
scanInProgress = true;
exec(
function(result) {
scanInProgress = false;
// work around bug in ZXing library
if (result.format === 'UPC_A' && result.text.length === 13) {
result.text = result.text.substring(1);
}
successCallback(result);
},
function(error) {
scanInProgress = false;
errorCallback(error);
},
'BarcodeScanner',
'scan',
config
);
};
//-------------------------------------------------------------------
BarcodeScanner.prototype.encode = function (type, data, successCallback, errorCallback, options) {
if (errorCallback == null) {
errorCallback = function () {
};
}
if (typeof errorCallback != "function") {
console.log("BarcodeScanner.encode failure: failure parameter not a function");
return;
}
if (typeof successCallback != "function") {
console.log("BarcodeScanner.encode failure: success callback parameter must be a function");
return;
}
exec(successCallback, errorCallback, 'BarcodeScanner', 'encode', [
{"type": type, "data": data, "options": options}
]);
};
var barcodeScanner = new BarcodeScanner();
module.exports = barcodeScanner;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment