cronapp-cordova-plugin-barcodescanner 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +16 -0
- package/LICENSE.txt +22 -0
- package/README.md +52 -0
- package/hooks/windows/check-arch.js +52 -0
- package/package.json +37 -0
- package/plugin.xml +70 -0
- package/spec/helper/cordova.js +83 -0
- package/spec/index.spec.js +78 -0
- package/src/android/README.md +1 -0
- package/src/android/barcodescanner-release-2.1.5.aar +0 -0
- package/src/android/barcodescanner.gradle +17 -0
- package/src/android/com/phonegap/plugins/barcodescanner/BarcodeScanner.java +328 -0
- package/src/browser/BarcodeScannerProxy.js +24 -0
- package/src/ios/CDVBarcodeScanner.bundle/beep.caf +0 -0
- package/src/ios/CDVBarcodeScanner.bundle/torch.png +0 -0
- package/src/ios/CDVBarcodeScanner.bundle/torch@2x.png +0 -0
- package/src/ios/CDVBarcodeScanner.bundle/torch@3x.png +0 -0
- package/src/ios/CDVBarcodeScanner.mm +1072 -0
- package/src/ios/scannerOverlay.xib +185 -0
- package/src/windows/BarcodeScannerProxy.js +738 -0
- package/src/windows/assets/plugin-barcodeScanner.css +89 -0
- package/src/windows/lib/Properties/AssemblyInfo.cs +39 -0
- package/src/windows/lib/Reader.cs +173 -0
- package/src/windows/lib/WinRTBarcodeReader.csproj +137 -0
- package/src/windows/lib/ZXing.winmd +0 -0
- package/src/windows/lib.UW/ANY/ZXing.winmd +0 -0
- package/src/windows/lib.UW/ARM/ZXing.winmd +0 -0
- package/src/windows/lib.UW/x64/ZXing.winmd +0 -0
- package/src/windows/lib.UW/x86/ZXing.winmd +0 -0
- package/www/barcodescanner.js +156 -0
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
|
3
|
+
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2011 Matt Kane. All rights reserved.
|
|
6
|
+
* Copyright (c) 2011, IBM Corporation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#import <AVFoundation/AVFoundation.h>
|
|
10
|
+
#import <AssetsLibrary/AssetsLibrary.h>
|
|
11
|
+
#import <Cordova/CDVPlugin.h>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
//------------------------------------------------------------------------------
|
|
15
|
+
// Delegate to handle orientation functions
|
|
16
|
+
//------------------------------------------------------------------------------
|
|
17
|
+
@protocol CDVBarcodeScannerOrientationDelegate <NSObject>
|
|
18
|
+
|
|
19
|
+
- (NSUInteger)supportedInterfaceOrientations;
|
|
20
|
+
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
|
|
21
|
+
- (BOOL)shouldAutorotate;
|
|
22
|
+
|
|
23
|
+
@end
|
|
24
|
+
|
|
25
|
+
//------------------------------------------------------------------------------
|
|
26
|
+
// Adds a shutter button to the UI, and changes the scan from continuous to
|
|
27
|
+
// only performing a scan when you click the shutter button. For testing.
|
|
28
|
+
//------------------------------------------------------------------------------
|
|
29
|
+
#define USE_SHUTTER 0
|
|
30
|
+
|
|
31
|
+
//------------------------------------------------------------------------------
|
|
32
|
+
@class CDVbcsProcessor;
|
|
33
|
+
@class CDVbcsViewController;
|
|
34
|
+
|
|
35
|
+
//------------------------------------------------------------------------------
|
|
36
|
+
// plugin class
|
|
37
|
+
//------------------------------------------------------------------------------
|
|
38
|
+
@interface CDVBarcodeScanner : CDVPlugin {}
|
|
39
|
+
- (NSString*)isScanNotPossible;
|
|
40
|
+
- (void)scan:(CDVInvokedUrlCommand*)command;
|
|
41
|
+
- (void)encode:(CDVInvokedUrlCommand*)command;
|
|
42
|
+
- (void)returnImage:(NSString*)filePath format:(NSString*)format callback:(NSString*)callback;
|
|
43
|
+
- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback;
|
|
44
|
+
- (void)returnError:(NSString*)message callback:(NSString*)callback;
|
|
45
|
+
@end
|
|
46
|
+
|
|
47
|
+
//------------------------------------------------------------------------------
|
|
48
|
+
// class that does the grunt work
|
|
49
|
+
//------------------------------------------------------------------------------
|
|
50
|
+
@interface CDVbcsProcessor : NSObject <AVCaptureMetadataOutputObjectsDelegate> {}
|
|
51
|
+
@property (nonatomic, retain) CDVBarcodeScanner* plugin;
|
|
52
|
+
@property (nonatomic, retain) NSString* callback;
|
|
53
|
+
@property (nonatomic, retain) UIViewController* parentViewController;
|
|
54
|
+
@property (nonatomic, retain) CDVbcsViewController* viewController;
|
|
55
|
+
@property (nonatomic, retain) AVCaptureSession* captureSession;
|
|
56
|
+
@property (nonatomic, retain) AVCaptureVideoPreviewLayer* previewLayer;
|
|
57
|
+
@property (nonatomic, retain) NSString* alternateXib;
|
|
58
|
+
@property (nonatomic, retain) NSMutableArray* results;
|
|
59
|
+
@property (nonatomic, retain) NSString* formats;
|
|
60
|
+
@property (nonatomic) BOOL is1D;
|
|
61
|
+
@property (nonatomic) BOOL is2D;
|
|
62
|
+
@property (nonatomic) BOOL capturing;
|
|
63
|
+
@property (nonatomic) BOOL isFrontCamera;
|
|
64
|
+
@property (nonatomic) BOOL isShowFlipCameraButton;
|
|
65
|
+
@property (nonatomic) BOOL isShowTorchButton;
|
|
66
|
+
@property (nonatomic) BOOL isFlipped;
|
|
67
|
+
@property (nonatomic) BOOL isTransitionAnimated;
|
|
68
|
+
@property (nonatomic) BOOL isSuccessBeepEnabled;
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback parentViewController:(UIViewController*)parentViewController alterateOverlayXib:(NSString *)alternateXib;
|
|
72
|
+
- (void)scanBarcode;
|
|
73
|
+
- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format;
|
|
74
|
+
- (void)barcodeScanFailed:(NSString*)message;
|
|
75
|
+
- (void)barcodeScanCancelled;
|
|
76
|
+
- (void)openDialog;
|
|
77
|
+
- (NSString*)setUpCaptureSession;
|
|
78
|
+
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection;
|
|
79
|
+
@end
|
|
80
|
+
|
|
81
|
+
//------------------------------------------------------------------------------
|
|
82
|
+
// Qr encoder processor
|
|
83
|
+
//------------------------------------------------------------------------------
|
|
84
|
+
@interface CDVqrProcessor: NSObject
|
|
85
|
+
@property (nonatomic, retain) CDVBarcodeScanner* plugin;
|
|
86
|
+
@property (nonatomic, retain) NSString* callback;
|
|
87
|
+
@property (nonatomic, retain) NSString* stringToEncode;
|
|
88
|
+
@property NSInteger size;
|
|
89
|
+
|
|
90
|
+
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback stringToEncode:(NSString*)stringToEncode;
|
|
91
|
+
- (void)generateImage;
|
|
92
|
+
@end
|
|
93
|
+
|
|
94
|
+
//------------------------------------------------------------------------------
|
|
95
|
+
// view controller for the ui
|
|
96
|
+
//------------------------------------------------------------------------------
|
|
97
|
+
@interface CDVbcsViewController : UIViewController <CDVBarcodeScannerOrientationDelegate> {}
|
|
98
|
+
@property (nonatomic, retain) CDVbcsProcessor* processor;
|
|
99
|
+
@property (nonatomic, retain) NSString* alternateXib;
|
|
100
|
+
@property (nonatomic) BOOL shutterPressed;
|
|
101
|
+
@property (nonatomic, retain) IBOutlet UIView* overlayView;
|
|
102
|
+
@property (nonatomic, retain) UIToolbar * toolbar;
|
|
103
|
+
@property (nonatomic, retain) UIView * reticleView;
|
|
104
|
+
// unsafe_unretained is equivalent to assign - used to prevent retain cycles in the property below
|
|
105
|
+
@property (nonatomic, unsafe_unretained) id orientationDelegate;
|
|
106
|
+
|
|
107
|
+
- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib;
|
|
108
|
+
- (void)startCapturing;
|
|
109
|
+
- (UIView*)buildOverlayView;
|
|
110
|
+
- (UIImage*)buildReticleImage;
|
|
111
|
+
- (void)shutterButtonPressed;
|
|
112
|
+
- (IBAction)cancelButtonPressed:(id)sender;
|
|
113
|
+
- (IBAction)flipCameraButtonPressed:(id)sender;
|
|
114
|
+
- (IBAction)torchButtonPressed:(id)sender;
|
|
115
|
+
|
|
116
|
+
@end
|
|
117
|
+
|
|
118
|
+
//------------------------------------------------------------------------------
|
|
119
|
+
// plugin class
|
|
120
|
+
//------------------------------------------------------------------------------
|
|
121
|
+
@implementation CDVBarcodeScanner
|
|
122
|
+
|
|
123
|
+
//--------------------------------------------------------------------------
|
|
124
|
+
- (NSString*)isScanNotPossible {
|
|
125
|
+
NSString* result = nil;
|
|
126
|
+
|
|
127
|
+
Class aClass = NSClassFromString(@"AVCaptureSession");
|
|
128
|
+
if (aClass == nil) {
|
|
129
|
+
return @"AVFoundation Framework not available";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
-(BOOL)notHasPermission
|
|
136
|
+
{
|
|
137
|
+
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
|
|
138
|
+
return (authStatus == AVAuthorizationStatusDenied ||
|
|
139
|
+
authStatus == AVAuthorizationStatusRestricted);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
-(BOOL)isUsageDescriptionSet
|
|
143
|
+
{
|
|
144
|
+
NSDictionary * plist = [[NSBundle mainBundle] infoDictionary];
|
|
145
|
+
if ([plist objectForKey:@"NSCameraUsageDescription" ] ||
|
|
146
|
+
[[NSBundle mainBundle] localizedStringForKey: @"NSCameraUsageDescription" value: nil table: @"InfoPlist"]) {
|
|
147
|
+
return YES;
|
|
148
|
+
}
|
|
149
|
+
return NO;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
//--------------------------------------------------------------------------
|
|
155
|
+
- (void)scan:(CDVInvokedUrlCommand*)command {
|
|
156
|
+
CDVbcsProcessor* processor;
|
|
157
|
+
NSString* callback;
|
|
158
|
+
NSString* capabilityError;
|
|
159
|
+
|
|
160
|
+
callback = command.callbackId;
|
|
161
|
+
|
|
162
|
+
NSDictionary* options;
|
|
163
|
+
if (command.arguments.count == 0) {
|
|
164
|
+
options = [NSDictionary dictionary];
|
|
165
|
+
} else {
|
|
166
|
+
options = command.arguments[0];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
BOOL preferFrontCamera = [options[@"preferFrontCamera"] boolValue];
|
|
170
|
+
BOOL showFlipCameraButton = [options[@"showFlipCameraButton"] boolValue];
|
|
171
|
+
BOOL showTorchButton = [options[@"showTorchButton"] boolValue];
|
|
172
|
+
BOOL disableAnimations = [options[@"disableAnimations"] boolValue];
|
|
173
|
+
BOOL disableSuccessBeep = [options[@"disableSuccessBeep"] boolValue];
|
|
174
|
+
|
|
175
|
+
// We allow the user to define an alternate xib file for loading the overlay.
|
|
176
|
+
NSString *overlayXib = options[@"overlayXib"];
|
|
177
|
+
|
|
178
|
+
capabilityError = [self isScanNotPossible];
|
|
179
|
+
if (capabilityError) {
|
|
180
|
+
[self returnError:capabilityError callback:callback];
|
|
181
|
+
return;
|
|
182
|
+
} else if ([self notHasPermission]) {
|
|
183
|
+
NSString * error = NSLocalizedString(@"Access to the camera has been prohibited; please enable it in the Settings app to continue.",nil);
|
|
184
|
+
[self returnError:error callback:callback];
|
|
185
|
+
return;
|
|
186
|
+
} else if (![self isUsageDescriptionSet]) {
|
|
187
|
+
NSString * error = NSLocalizedString(@"NSCameraUsageDescription is not set in the info.plist", nil);
|
|
188
|
+
[self returnError:error callback:callback];
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
processor = [[CDVbcsProcessor alloc]
|
|
193
|
+
initWithPlugin:self
|
|
194
|
+
callback:callback
|
|
195
|
+
parentViewController:self.viewController
|
|
196
|
+
alterateOverlayXib:overlayXib
|
|
197
|
+
];
|
|
198
|
+
// queue [processor scanBarcode] to run on the event loop
|
|
199
|
+
|
|
200
|
+
if (preferFrontCamera) {
|
|
201
|
+
processor.isFrontCamera = true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (showFlipCameraButton) {
|
|
205
|
+
processor.isShowFlipCameraButton = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (showTorchButton) {
|
|
209
|
+
processor.isShowTorchButton = true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
processor.isSuccessBeepEnabled = !disableSuccessBeep;
|
|
213
|
+
|
|
214
|
+
processor.isTransitionAnimated = !disableAnimations;
|
|
215
|
+
|
|
216
|
+
processor.formats = options[@"formats"];
|
|
217
|
+
|
|
218
|
+
[processor performSelector:@selector(scanBarcode) withObject:nil afterDelay:0];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
//--------------------------------------------------------------------------
|
|
222
|
+
- (void)encode:(CDVInvokedUrlCommand*)command {
|
|
223
|
+
if([command.arguments count] < 1)
|
|
224
|
+
[self returnError:@"Too few arguments!" callback:command.callbackId];
|
|
225
|
+
|
|
226
|
+
CDVqrProcessor* processor;
|
|
227
|
+
NSString* callback;
|
|
228
|
+
callback = command.callbackId;
|
|
229
|
+
|
|
230
|
+
processor = [[CDVqrProcessor alloc]
|
|
231
|
+
initWithPlugin:self
|
|
232
|
+
callback:callback
|
|
233
|
+
stringToEncode: command.arguments[0][@"data"]
|
|
234
|
+
];
|
|
235
|
+
// queue [processor generateImage] to run on the event loop
|
|
236
|
+
[processor performSelector:@selector(generateImage) withObject:nil afterDelay:0];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
- (void)returnImage:(NSString*)filePath format:(NSString*)format callback:(NSString*)callback{
|
|
240
|
+
NSMutableDictionary* resultDict = [[NSMutableDictionary alloc] init];
|
|
241
|
+
resultDict[@"format"] = format;
|
|
242
|
+
resultDict[@"file"] = filePath;
|
|
243
|
+
|
|
244
|
+
CDVPluginResult* result = [CDVPluginResult
|
|
245
|
+
resultWithStatus: CDVCommandStatus_OK
|
|
246
|
+
messageAsDictionary:resultDict
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
[[self commandDelegate] sendPluginResult:result callbackId:callback];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
//--------------------------------------------------------------------------
|
|
253
|
+
- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback{
|
|
254
|
+
NSNumber* cancelledNumber = @(cancelled ? 1 : 0);
|
|
255
|
+
|
|
256
|
+
NSMutableDictionary* resultDict = [NSMutableDictionary new];
|
|
257
|
+
resultDict[@"text"] = scannedText;
|
|
258
|
+
resultDict[@"format"] = format;
|
|
259
|
+
resultDict[@"cancelled"] = cancelledNumber;
|
|
260
|
+
|
|
261
|
+
CDVPluginResult* result = [CDVPluginResult
|
|
262
|
+
resultWithStatus: CDVCommandStatus_OK
|
|
263
|
+
messageAsDictionary: resultDict
|
|
264
|
+
];
|
|
265
|
+
[self.commandDelegate sendPluginResult:result callbackId:callback];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
//--------------------------------------------------------------------------
|
|
269
|
+
- (void)returnError:(NSString*)message callback:(NSString*)callback {
|
|
270
|
+
CDVPluginResult* result = [CDVPluginResult
|
|
271
|
+
resultWithStatus: CDVCommandStatus_ERROR
|
|
272
|
+
messageAsString: message
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
[self.commandDelegate sendPluginResult:result callbackId:callback];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@end
|
|
279
|
+
|
|
280
|
+
//------------------------------------------------------------------------------
|
|
281
|
+
// class that does the grunt work
|
|
282
|
+
//------------------------------------------------------------------------------
|
|
283
|
+
@implementation CDVbcsProcessor
|
|
284
|
+
|
|
285
|
+
@synthesize plugin = _plugin;
|
|
286
|
+
@synthesize callback = _callback;
|
|
287
|
+
@synthesize parentViewController = _parentViewController;
|
|
288
|
+
@synthesize viewController = _viewController;
|
|
289
|
+
@synthesize captureSession = _captureSession;
|
|
290
|
+
@synthesize previewLayer = _previewLayer;
|
|
291
|
+
@synthesize alternateXib = _alternateXib;
|
|
292
|
+
@synthesize is1D = _is1D;
|
|
293
|
+
@synthesize is2D = _is2D;
|
|
294
|
+
@synthesize capturing = _capturing;
|
|
295
|
+
@synthesize results = _results;
|
|
296
|
+
|
|
297
|
+
SystemSoundID _soundFileObject;
|
|
298
|
+
|
|
299
|
+
//--------------------------------------------------------------------------
|
|
300
|
+
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin
|
|
301
|
+
callback:(NSString*)callback
|
|
302
|
+
parentViewController:(UIViewController*)parentViewController
|
|
303
|
+
alterateOverlayXib:(NSString *)alternateXib {
|
|
304
|
+
self = [super init];
|
|
305
|
+
if (!self) return self;
|
|
306
|
+
|
|
307
|
+
self.plugin = plugin;
|
|
308
|
+
self.callback = callback;
|
|
309
|
+
self.parentViewController = parentViewController;
|
|
310
|
+
self.alternateXib = alternateXib;
|
|
311
|
+
|
|
312
|
+
self.is1D = YES;
|
|
313
|
+
self.is2D = YES;
|
|
314
|
+
self.capturing = NO;
|
|
315
|
+
self.results = [NSMutableArray new];
|
|
316
|
+
|
|
317
|
+
CFURLRef soundFileURLRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("CDVBarcodeScanner.bundle/beep"), CFSTR ("caf"), NULL);
|
|
318
|
+
AudioServicesCreateSystemSoundID(soundFileURLRef, &_soundFileObject);
|
|
319
|
+
|
|
320
|
+
return self;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
//--------------------------------------------------------------------------
|
|
324
|
+
- (void)dealloc {
|
|
325
|
+
self.plugin = nil;
|
|
326
|
+
self.callback = nil;
|
|
327
|
+
self.parentViewController = nil;
|
|
328
|
+
self.viewController = nil;
|
|
329
|
+
self.captureSession = nil;
|
|
330
|
+
self.previewLayer = nil;
|
|
331
|
+
self.alternateXib = nil;
|
|
332
|
+
self.results = nil;
|
|
333
|
+
|
|
334
|
+
self.capturing = NO;
|
|
335
|
+
|
|
336
|
+
AudioServicesRemoveSystemSoundCompletion(_soundFileObject);
|
|
337
|
+
AudioServicesDisposeSystemSoundID(_soundFileObject);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
//--------------------------------------------------------------------------
|
|
341
|
+
- (void)scanBarcode {
|
|
342
|
+
|
|
343
|
+
// self.captureSession = nil;
|
|
344
|
+
// self.previewLayer = nil;
|
|
345
|
+
NSString* errorMessage = [self setUpCaptureSession];
|
|
346
|
+
if (errorMessage) {
|
|
347
|
+
[self barcodeScanFailed:errorMessage];
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
self.viewController = [[CDVbcsViewController alloc] initWithProcessor: self alternateOverlay:self.alternateXib];
|
|
352
|
+
// here we set the orientation delegate to the MainViewController of the app (orientation controlled in the Project Settings)
|
|
353
|
+
self.viewController.orientationDelegate = self.plugin.viewController;
|
|
354
|
+
|
|
355
|
+
// delayed [self openDialog];
|
|
356
|
+
[self performSelector:@selector(openDialog) withObject:nil afterDelay:1];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
//--------------------------------------------------------------------------
|
|
360
|
+
- (void)openDialog {
|
|
361
|
+
[self.parentViewController
|
|
362
|
+
presentViewController:self.viewController
|
|
363
|
+
animated:self.isTransitionAnimated completion:nil
|
|
364
|
+
];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
//--------------------------------------------------------------------------
|
|
368
|
+
- (void)barcodeScanDone:(void (^)(void))callbackBlock {
|
|
369
|
+
self.capturing = NO;
|
|
370
|
+
[self.captureSession stopRunning];
|
|
371
|
+
[self.parentViewController dismissViewControllerAnimated:self.isTransitionAnimated completion:callbackBlock];
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
|
375
|
+
[device lockForConfiguration:nil];
|
|
376
|
+
if([device isAutoFocusRangeRestrictionSupported]) {
|
|
377
|
+
[device setAutoFocusRangeRestriction:AVCaptureAutoFocusRangeRestrictionNone];
|
|
378
|
+
}
|
|
379
|
+
[device unlockForConfiguration];
|
|
380
|
+
|
|
381
|
+
// viewcontroller holding onto a reference to us, release them so they
|
|
382
|
+
// will release us
|
|
383
|
+
self.viewController = nil;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
//--------------------------------------------------------------------------
|
|
387
|
+
- (BOOL)checkResult:(NSString *)result {
|
|
388
|
+
[self.results addObject:result];
|
|
389
|
+
|
|
390
|
+
NSInteger treshold = 7;
|
|
391
|
+
|
|
392
|
+
if (self.results.count > treshold) {
|
|
393
|
+
[self.results removeObjectAtIndex:0];
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (self.results.count < treshold)
|
|
397
|
+
{
|
|
398
|
+
return NO;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
BOOL allEqual = YES;
|
|
402
|
+
NSString *compareString = self.results[0];
|
|
403
|
+
|
|
404
|
+
for (NSString *aResult in self.results)
|
|
405
|
+
{
|
|
406
|
+
if (![compareString isEqualToString:aResult])
|
|
407
|
+
{
|
|
408
|
+
allEqual = NO;
|
|
409
|
+
//NSLog(@"Did not fit: %@",self.results);
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return allEqual;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
//--------------------------------------------------------------------------
|
|
418
|
+
- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format {
|
|
419
|
+
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
420
|
+
if (self.isSuccessBeepEnabled) {
|
|
421
|
+
AudioServicesPlaySystemSound(_soundFileObject);
|
|
422
|
+
}
|
|
423
|
+
[self barcodeScanDone:^{
|
|
424
|
+
[self.plugin returnSuccess:text format:format cancelled:FALSE flipped:FALSE callback:self.callback];
|
|
425
|
+
}];
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
//--------------------------------------------------------------------------
|
|
430
|
+
- (void)barcodeScanFailed:(NSString*)message {
|
|
431
|
+
dispatch_block_t block = ^{
|
|
432
|
+
[self barcodeScanDone:^{
|
|
433
|
+
[self.plugin returnError:message callback:self.callback];
|
|
434
|
+
}];
|
|
435
|
+
};
|
|
436
|
+
if ([NSThread isMainThread]) {
|
|
437
|
+
block();
|
|
438
|
+
} else {
|
|
439
|
+
dispatch_sync(dispatch_get_main_queue(), block);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
//--------------------------------------------------------------------------
|
|
444
|
+
- (void)barcodeScanCancelled {
|
|
445
|
+
[self barcodeScanDone:^{
|
|
446
|
+
[self.plugin returnSuccess:@"" format:@"" cancelled:TRUE flipped:self.isFlipped callback:self.callback];
|
|
447
|
+
}];
|
|
448
|
+
if (self.isFlipped) {
|
|
449
|
+
self.isFlipped = NO;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
- (void)flipCamera {
|
|
454
|
+
self.isFlipped = YES;
|
|
455
|
+
self.isFrontCamera = !self.isFrontCamera;
|
|
456
|
+
[self barcodeScanDone:^{
|
|
457
|
+
if (self.isFlipped) {
|
|
458
|
+
self.isFlipped = NO;
|
|
459
|
+
}
|
|
460
|
+
[self performSelector:@selector(scanBarcode) withObject:nil afterDelay:0.1];
|
|
461
|
+
}];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
- (void)toggleTorch {
|
|
465
|
+
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
|
466
|
+
[device lockForConfiguration:nil];
|
|
467
|
+
if (device.flashActive) {
|
|
468
|
+
[device setTorchMode:AVCaptureTorchModeOff];
|
|
469
|
+
[device setFlashMode:AVCaptureFlashModeOff];
|
|
470
|
+
} else {
|
|
471
|
+
[device setTorchModeOnWithLevel:AVCaptureMaxAvailableTorchLevel error:nil];
|
|
472
|
+
[device setFlashMode:AVCaptureFlashModeOn];
|
|
473
|
+
}
|
|
474
|
+
[device unlockForConfiguration];
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
//--------------------------------------------------------------------------
|
|
478
|
+
- (NSString*)setUpCaptureSession {
|
|
479
|
+
NSError* error = nil;
|
|
480
|
+
|
|
481
|
+
AVCaptureSession* captureSession = [[AVCaptureSession alloc] init];
|
|
482
|
+
self.captureSession = captureSession;
|
|
483
|
+
|
|
484
|
+
AVCaptureDevice* __block device = nil;
|
|
485
|
+
if (self.isFrontCamera) {
|
|
486
|
+
|
|
487
|
+
NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
|
488
|
+
[devices enumerateObjectsUsingBlock:^(AVCaptureDevice *obj, NSUInteger idx, BOOL *stop) {
|
|
489
|
+
if (obj.position == AVCaptureDevicePositionFront) {
|
|
490
|
+
device = obj;
|
|
491
|
+
}
|
|
492
|
+
}];
|
|
493
|
+
} else {
|
|
494
|
+
device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
|
495
|
+
if (!device) return @"unable to obtain video capture device";
|
|
496
|
+
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// set focus params if available to improve focusing
|
|
500
|
+
[device lockForConfiguration:&error];
|
|
501
|
+
if (error == nil) {
|
|
502
|
+
if([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
|
503
|
+
[device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
|
|
504
|
+
}
|
|
505
|
+
if([device isAutoFocusRangeRestrictionSupported]) {
|
|
506
|
+
[device setAutoFocusRangeRestriction:AVCaptureAutoFocusRangeRestrictionNear];
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
[device unlockForConfiguration];
|
|
510
|
+
|
|
511
|
+
AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
|
|
512
|
+
if (!input) return @"unable to obtain video capture device input";
|
|
513
|
+
|
|
514
|
+
AVCaptureMetadataOutput* output = [[AVCaptureMetadataOutput alloc] init];
|
|
515
|
+
if (!output) return @"unable to obtain video capture output";
|
|
516
|
+
|
|
517
|
+
[output setMetadataObjectsDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)];
|
|
518
|
+
|
|
519
|
+
if ([captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
|
|
520
|
+
captureSession.sessionPreset = AVCaptureSessionPresetHigh;
|
|
521
|
+
} else if ([captureSession canSetSessionPreset:AVCaptureSessionPresetMedium]) {
|
|
522
|
+
captureSession.sessionPreset = AVCaptureSessionPresetMedium;
|
|
523
|
+
} else {
|
|
524
|
+
return @"unable to preset high nor medium quality video capture";
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if ([captureSession canAddInput:input]) {
|
|
528
|
+
[captureSession addInput:input];
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
return @"unable to add video capture device input to session";
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if ([captureSession canAddOutput:output]) {
|
|
535
|
+
[captureSession addOutput:output];
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
return @"unable to add video capture output to session";
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
[output setMetadataObjectTypes:[self formatObjectTypes]];
|
|
542
|
+
|
|
543
|
+
// setup capture preview layer
|
|
544
|
+
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
|
|
545
|
+
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
|
546
|
+
|
|
547
|
+
// run on next event loop pass [captureSession startRunning]
|
|
548
|
+
[captureSession performSelector:@selector(startRunning) withObject:nil afterDelay:0];
|
|
549
|
+
|
|
550
|
+
return nil;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
//--------------------------------------------------------------------------
|
|
554
|
+
// this method gets sent the captured frames
|
|
555
|
+
//--------------------------------------------------------------------------
|
|
556
|
+
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection*)connection {
|
|
557
|
+
|
|
558
|
+
if (!self.capturing) return;
|
|
559
|
+
|
|
560
|
+
#if USE_SHUTTER
|
|
561
|
+
if (!self.viewController.shutterPressed) return;
|
|
562
|
+
self.viewController.shutterPressed = NO;
|
|
563
|
+
|
|
564
|
+
UIView* flashView = [[UIView alloc] initWithFrame:self.viewController.view.frame];
|
|
565
|
+
[flashView setBackgroundColor:[UIColor whiteColor]];
|
|
566
|
+
[self.viewController.view.window addSubview:flashView];
|
|
567
|
+
|
|
568
|
+
[UIView
|
|
569
|
+
animateWithDuration:.4f
|
|
570
|
+
animations:^{
|
|
571
|
+
[flashView setAlpha:0.f];
|
|
572
|
+
}
|
|
573
|
+
completion:^(BOOL finished){
|
|
574
|
+
[flashView removeFromSuperview];
|
|
575
|
+
}
|
|
576
|
+
];
|
|
577
|
+
#endif
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
try {
|
|
581
|
+
// This will bring in multiple entities if there are multiple 2D codes in frame.
|
|
582
|
+
for (AVMetadataObject *metaData in metadataObjects) {
|
|
583
|
+
AVMetadataMachineReadableCodeObject* code = (AVMetadataMachineReadableCodeObject*)[self.previewLayer transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject*)metaData];
|
|
584
|
+
|
|
585
|
+
if ([self checkResult:code.stringValue]) {
|
|
586
|
+
[self barcodeScanSucceeded:code.stringValue format:[self formatStringFromMetadata:code]];
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
catch (...) {
|
|
591
|
+
// NSLog(@"decoding: unknown exception");
|
|
592
|
+
// [self barcodeScanFailed:@"unknown exception decoding barcode"];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// NSTimeInterval timeElapsed = [NSDate timeIntervalSinceReferenceDate] - timeStart;
|
|
596
|
+
// NSLog(@"decoding completed in %dms", (int) (timeElapsed * 1000));
|
|
597
|
+
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
//--------------------------------------------------------------------------
|
|
601
|
+
// convert metadata object information to barcode format string
|
|
602
|
+
//--------------------------------------------------------------------------
|
|
603
|
+
- (NSString*)formatStringFromMetadata:(AVMetadataMachineReadableCodeObject*)format {
|
|
604
|
+
if (format.type == AVMetadataObjectTypeQRCode) return @"QR_CODE";
|
|
605
|
+
if (format.type == AVMetadataObjectTypeAztecCode) return @"AZTEC";
|
|
606
|
+
if (format.type == AVMetadataObjectTypeDataMatrixCode) return @"DATA_MATRIX";
|
|
607
|
+
if (format.type == AVMetadataObjectTypeUPCECode) return @"UPC_E";
|
|
608
|
+
// According to Apple documentation, UPC_A is EAN13 with a leading 0.
|
|
609
|
+
if (format.type == AVMetadataObjectTypeEAN13Code && [format.stringValue characterAtIndex:0] == '0') return @"UPC_A";
|
|
610
|
+
if (format.type == AVMetadataObjectTypeEAN8Code) return @"EAN_8";
|
|
611
|
+
if (format.type == AVMetadataObjectTypeEAN13Code) return @"EAN_13";
|
|
612
|
+
if (format.type == AVMetadataObjectTypeCode128Code) return @"CODE_128";
|
|
613
|
+
if (format.type == AVMetadataObjectTypeCode93Code) return @"CODE_93";
|
|
614
|
+
if (format.type == AVMetadataObjectTypeCode39Code) return @"CODE_39";
|
|
615
|
+
if (format.type == AVMetadataObjectTypeInterleaved2of5Code) return @"ITF";
|
|
616
|
+
if (format.type == AVMetadataObjectTypeITF14Code) return @"ITF_14";
|
|
617
|
+
|
|
618
|
+
if (format.type == AVMetadataObjectTypePDF417Code) return @"PDF_417";
|
|
619
|
+
return @"???";
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
//--------------------------------------------------------------------------
|
|
623
|
+
// convert string formats to metadata objects
|
|
624
|
+
//--------------------------------------------------------------------------
|
|
625
|
+
- (NSArray*) formatObjectTypes {
|
|
626
|
+
NSArray *supportedFormats = nil;
|
|
627
|
+
if (self.formats != nil) {
|
|
628
|
+
supportedFormats = [self.formats componentsSeparatedByString:@","];
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
NSMutableArray * formatObjectTypes = [NSMutableArray array];
|
|
632
|
+
|
|
633
|
+
if (self.formats == nil || [supportedFormats containsObject:@"QR_CODE"]) [formatObjectTypes addObject:AVMetadataObjectTypeQRCode];
|
|
634
|
+
if (self.formats == nil || [supportedFormats containsObject:@"AZTEC"]) [formatObjectTypes addObject:AVMetadataObjectTypeAztecCode];
|
|
635
|
+
if (self.formats == nil || [supportedFormats containsObject:@"DATA_MATRIX"]) [formatObjectTypes addObject:AVMetadataObjectTypeDataMatrixCode];
|
|
636
|
+
if (self.formats == nil || [supportedFormats containsObject:@"UPC_E"]) [formatObjectTypes addObject:AVMetadataObjectTypeUPCECode];
|
|
637
|
+
if (self.formats == nil || [supportedFormats containsObject:@"EAN_8"]) [formatObjectTypes addObject:AVMetadataObjectTypeEAN8Code];
|
|
638
|
+
if (self.formats == nil || [supportedFormats containsObject:@"EAN_13"]) [formatObjectTypes addObject:AVMetadataObjectTypeEAN13Code];
|
|
639
|
+
if (self.formats == nil || [supportedFormats containsObject:@"CODE_128"]) [formatObjectTypes addObject:AVMetadataObjectTypeCode128Code];
|
|
640
|
+
if (self.formats == nil || [supportedFormats containsObject:@"CODE_93"]) [formatObjectTypes addObject:AVMetadataObjectTypeCode93Code];
|
|
641
|
+
if (self.formats == nil || [supportedFormats containsObject:@"CODE_39"]) [formatObjectTypes addObject:AVMetadataObjectTypeCode39Code];
|
|
642
|
+
if (self.formats == nil || [supportedFormats containsObject:@"ITF"]) [formatObjectTypes addObject:AVMetadataObjectTypeInterleaved2of5Code];
|
|
643
|
+
if (self.formats == nil || [supportedFormats containsObject:@"ITF_14"]) [formatObjectTypes addObject:AVMetadataObjectTypeITF14Code];
|
|
644
|
+
if (self.formats == nil || [supportedFormats containsObject:@"PDF_417"]) [formatObjectTypes addObject:AVMetadataObjectTypePDF417Code];
|
|
645
|
+
|
|
646
|
+
return formatObjectTypes;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
@end
|
|
650
|
+
|
|
651
|
+
//------------------------------------------------------------------------------
|
|
652
|
+
// qr encoder processor
|
|
653
|
+
//------------------------------------------------------------------------------
|
|
654
|
+
@implementation CDVqrProcessor
|
|
655
|
+
@synthesize plugin = _plugin;
|
|
656
|
+
@synthesize callback = _callback;
|
|
657
|
+
@synthesize stringToEncode = _stringToEncode;
|
|
658
|
+
@synthesize size = _size;
|
|
659
|
+
|
|
660
|
+
- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback stringToEncode:(NSString*)stringToEncode{
|
|
661
|
+
self = [super init];
|
|
662
|
+
if (!self) return self;
|
|
663
|
+
|
|
664
|
+
self.plugin = plugin;
|
|
665
|
+
self.callback = callback;
|
|
666
|
+
self.stringToEncode = stringToEncode;
|
|
667
|
+
self.size = 300;
|
|
668
|
+
|
|
669
|
+
return self;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
//--------------------------------------------------------------------------
|
|
673
|
+
- (void)dealloc {
|
|
674
|
+
self.plugin = nil;
|
|
675
|
+
self.callback = nil;
|
|
676
|
+
self.stringToEncode = nil;
|
|
677
|
+
}
|
|
678
|
+
//--------------------------------------------------------------------------
|
|
679
|
+
- (void)generateImage{
|
|
680
|
+
/* setup qr filter */
|
|
681
|
+
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
|
|
682
|
+
[filter setDefaults];
|
|
683
|
+
|
|
684
|
+
/* set filter's input message
|
|
685
|
+
* the encoding string has to be convert to a UTF-8 encoded NSData object */
|
|
686
|
+
[filter setValue:[self.stringToEncode dataUsingEncoding:NSUTF8StringEncoding]
|
|
687
|
+
forKey:@"inputMessage"];
|
|
688
|
+
|
|
689
|
+
/* on ios >= 7.0 set low image error correction level */
|
|
690
|
+
if (floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_7_0)
|
|
691
|
+
[filter setValue:@"L" forKey:@"inputCorrectionLevel"];
|
|
692
|
+
|
|
693
|
+
/* prepare cgImage */
|
|
694
|
+
CIImage *outputImage = [filter outputImage];
|
|
695
|
+
CIContext *context = [CIContext contextWithOptions:nil];
|
|
696
|
+
CGImageRef cgImage = [context createCGImage:outputImage
|
|
697
|
+
fromRect:[outputImage extent]];
|
|
698
|
+
|
|
699
|
+
/* returned qr code image */
|
|
700
|
+
UIImage *qrImage = [UIImage imageWithCGImage:cgImage
|
|
701
|
+
scale:1.
|
|
702
|
+
orientation:UIImageOrientationUp];
|
|
703
|
+
/* resize generated image */
|
|
704
|
+
CGFloat width = _size;
|
|
705
|
+
CGFloat height = _size;
|
|
706
|
+
|
|
707
|
+
UIGraphicsBeginImageContext(CGSizeMake(width, height));
|
|
708
|
+
|
|
709
|
+
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
|
710
|
+
CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);
|
|
711
|
+
[qrImage drawInRect:CGRectMake(0, 0, width, height)];
|
|
712
|
+
qrImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
713
|
+
|
|
714
|
+
/* clean up */
|
|
715
|
+
UIGraphicsEndImageContext();
|
|
716
|
+
CGImageRelease(cgImage);
|
|
717
|
+
|
|
718
|
+
/* save image to file */
|
|
719
|
+
NSString* fileName = [[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingString:@".jpg"];
|
|
720
|
+
NSString* filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
|
|
721
|
+
[UIImageJPEGRepresentation(qrImage, 1.0) writeToFile:filePath atomically:YES];
|
|
722
|
+
|
|
723
|
+
/* return file path back to cordova */
|
|
724
|
+
[self.plugin returnImage:filePath format:@"QR_CODE" callback: self.callback];
|
|
725
|
+
}
|
|
726
|
+
@end
|
|
727
|
+
|
|
728
|
+
//------------------------------------------------------------------------------
|
|
729
|
+
// view controller for the ui
|
|
730
|
+
//------------------------------------------------------------------------------
|
|
731
|
+
@implementation CDVbcsViewController
|
|
732
|
+
@synthesize processor = _processor;
|
|
733
|
+
@synthesize shutterPressed = _shutterPressed;
|
|
734
|
+
@synthesize alternateXib = _alternateXib;
|
|
735
|
+
@synthesize overlayView = _overlayView;
|
|
736
|
+
|
|
737
|
+
//--------------------------------------------------------------------------
|
|
738
|
+
- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib {
|
|
739
|
+
self = [super init];
|
|
740
|
+
if (!self) return self;
|
|
741
|
+
|
|
742
|
+
self.processor = processor;
|
|
743
|
+
self.shutterPressed = NO;
|
|
744
|
+
self.alternateXib = alternateXib;
|
|
745
|
+
self.overlayView = nil;
|
|
746
|
+
return self;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
//--------------------------------------------------------------------------
|
|
750
|
+
- (void)dealloc {
|
|
751
|
+
self.view = nil;
|
|
752
|
+
self.processor = nil;
|
|
753
|
+
self.shutterPressed = NO;
|
|
754
|
+
self.alternateXib = nil;
|
|
755
|
+
self.overlayView = nil;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
//--------------------------------------------------------------------------
|
|
759
|
+
- (void)loadView {
|
|
760
|
+
self.view = [[UIView alloc] initWithFrame: self.processor.parentViewController.view.frame];
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
//--------------------------------------------------------------------------
|
|
764
|
+
- (void)viewWillAppear:(BOOL)animated {
|
|
765
|
+
|
|
766
|
+
// set video orientation to what the camera sees
|
|
767
|
+
self.processor.previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
|
|
768
|
+
|
|
769
|
+
// this fixes the bug when the statusbar is landscape, and the preview layer
|
|
770
|
+
// starts up in portrait (not filling the whole view)
|
|
771
|
+
self.processor.previewLayer.frame = self.view.bounds;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
//--------------------------------------------------------------------------
|
|
775
|
+
- (void)viewDidAppear:(BOOL)animated {
|
|
776
|
+
// setup capture preview layer
|
|
777
|
+
AVCaptureVideoPreviewLayer* previewLayer = self.processor.previewLayer;
|
|
778
|
+
previewLayer.frame = self.view.bounds;
|
|
779
|
+
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
|
780
|
+
|
|
781
|
+
if ([previewLayer.connection isVideoOrientationSupported]) {
|
|
782
|
+
previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
[self.view.layer insertSublayer:previewLayer below:[[self.view.layer sublayers] objectAtIndex:0]];
|
|
786
|
+
|
|
787
|
+
[self.view addSubview:[self buildOverlayView]];
|
|
788
|
+
[self startCapturing];
|
|
789
|
+
|
|
790
|
+
[super viewDidAppear:animated];
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:(UIInterfaceOrientation)orientation {
|
|
794
|
+
switch (orientation) {
|
|
795
|
+
case UIInterfaceOrientationPortrait:
|
|
796
|
+
return AVCaptureVideoOrientationPortrait;
|
|
797
|
+
case UIInterfaceOrientationPortraitUpsideDown:
|
|
798
|
+
return AVCaptureVideoOrientationPortraitUpsideDown;
|
|
799
|
+
case UIInterfaceOrientationLandscapeLeft:
|
|
800
|
+
return AVCaptureVideoOrientationLandscapeLeft;
|
|
801
|
+
case UIInterfaceOrientationLandscapeRight:
|
|
802
|
+
return AVCaptureVideoOrientationLandscapeRight;
|
|
803
|
+
default:
|
|
804
|
+
return AVCaptureVideoOrientationPortrait;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
//--------------------------------------------------------------------------
|
|
809
|
+
- (void)startCapturing {
|
|
810
|
+
self.processor.capturing = YES;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
//--------------------------------------------------------------------------
|
|
814
|
+
- (IBAction)shutterButtonPressed {
|
|
815
|
+
self.shutterPressed = YES;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
//--------------------------------------------------------------------------
|
|
819
|
+
- (IBAction)cancelButtonPressed:(id)sender {
|
|
820
|
+
[self.processor performSelector:@selector(barcodeScanCancelled) withObject:nil afterDelay:0];
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
- (IBAction)flipCameraButtonPressed:(id)sender
|
|
824
|
+
{
|
|
825
|
+
[self.processor performSelector:@selector(flipCamera) withObject:nil afterDelay:0];
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
- (IBAction)torchButtonPressed:(id)sender
|
|
829
|
+
{
|
|
830
|
+
[self.processor performSelector:@selector(toggleTorch) withObject:nil afterDelay:0];
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
//--------------------------------------------------------------------------
|
|
834
|
+
- (UIView *)buildOverlayViewFromXib
|
|
835
|
+
{
|
|
836
|
+
[[NSBundle mainBundle] loadNibNamed:self.alternateXib owner:self options:NULL];
|
|
837
|
+
|
|
838
|
+
if ( self.overlayView == nil )
|
|
839
|
+
{
|
|
840
|
+
NSLog(@"%@", @"An error occurred loading the overlay xib. It appears that the overlayView outlet is not set.");
|
|
841
|
+
return nil;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
self.overlayView.autoresizesSubviews = YES;
|
|
845
|
+
self.overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
846
|
+
self.overlayView.opaque = NO;
|
|
847
|
+
|
|
848
|
+
CGRect bounds = self.view.bounds;
|
|
849
|
+
bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
|
|
850
|
+
|
|
851
|
+
[self.overlayView setFrame:bounds];
|
|
852
|
+
|
|
853
|
+
return self.overlayView;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
//--------------------------------------------------------------------------
|
|
857
|
+
- (UIView*)buildOverlayView {
|
|
858
|
+
|
|
859
|
+
if ( nil != self.alternateXib )
|
|
860
|
+
{
|
|
861
|
+
return [self buildOverlayViewFromXib];
|
|
862
|
+
}
|
|
863
|
+
CGRect bounds = self.view.frame;
|
|
864
|
+
bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
|
|
865
|
+
|
|
866
|
+
UIView* overlayView = [[UIView alloc] initWithFrame:bounds];
|
|
867
|
+
overlayView.autoresizesSubviews = YES;
|
|
868
|
+
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
869
|
+
overlayView.opaque = NO;
|
|
870
|
+
|
|
871
|
+
self.toolbar = [[UIToolbar alloc] init];
|
|
872
|
+
self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
873
|
+
|
|
874
|
+
id cancelButton = [[UIBarButtonItem alloc]
|
|
875
|
+
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
|
|
876
|
+
target:(id)self
|
|
877
|
+
action:@selector(cancelButtonPressed:)
|
|
878
|
+
];
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
id flexSpace = [[UIBarButtonItem alloc]
|
|
882
|
+
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
|
883
|
+
target:nil
|
|
884
|
+
action:nil
|
|
885
|
+
];
|
|
886
|
+
|
|
887
|
+
id flipCamera = [[UIBarButtonItem alloc]
|
|
888
|
+
initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
|
|
889
|
+
target:(id)self
|
|
890
|
+
action:@selector(flipCameraButtonPressed:)
|
|
891
|
+
];
|
|
892
|
+
|
|
893
|
+
NSMutableArray *items;
|
|
894
|
+
|
|
895
|
+
#if USE_SHUTTER
|
|
896
|
+
id shutterButton = [[UIBarButtonItem alloc]
|
|
897
|
+
initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
|
|
898
|
+
target:(id)self
|
|
899
|
+
action:@selector(shutterButtonPressed)
|
|
900
|
+
];
|
|
901
|
+
|
|
902
|
+
if (_processor.isShowFlipCameraButton) {
|
|
903
|
+
items = [NSMutableArray arrayWithObjects:flexSpace, cancelButton, flexSpace, flipCamera, shutterButton, nil];
|
|
904
|
+
} else {
|
|
905
|
+
items = [NSMutableArray arrayWithObjects:flexSpace, cancelButton, flexSpace, shutterButton, nil];
|
|
906
|
+
}
|
|
907
|
+
#else
|
|
908
|
+
if (_processor.isShowFlipCameraButton) {
|
|
909
|
+
items = [@[flexSpace, cancelButton, flexSpace, flipCamera] mutableCopy];
|
|
910
|
+
} else {
|
|
911
|
+
items = [@[flexSpace, cancelButton, flexSpace] mutableCopy];
|
|
912
|
+
}
|
|
913
|
+
#endif
|
|
914
|
+
|
|
915
|
+
if (_processor.isShowTorchButton && !_processor.isFrontCamera) {
|
|
916
|
+
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
|
917
|
+
if ([device hasTorch] && [device hasFlash]) {
|
|
918
|
+
NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"CDVBarcodeScanner" withExtension:@"bundle"];
|
|
919
|
+
NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
|
|
920
|
+
NSString *imagePath = [bundle pathForResource:@"torch" ofType:@"png"];
|
|
921
|
+
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
|
|
922
|
+
|
|
923
|
+
id torchButton = [[UIBarButtonItem alloc]
|
|
924
|
+
initWithImage:image
|
|
925
|
+
style:UIBarButtonItemStylePlain
|
|
926
|
+
target:(id)self
|
|
927
|
+
action:@selector(torchButtonPressed:)
|
|
928
|
+
];
|
|
929
|
+
|
|
930
|
+
[items insertObject:torchButton atIndex:0];
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
self.toolbar.items = items;
|
|
934
|
+
[overlayView addSubview: self.toolbar];
|
|
935
|
+
|
|
936
|
+
UIImage* reticleImage = [self buildReticleImage];
|
|
937
|
+
self.reticleView = [[UIImageView alloc] initWithImage:reticleImage];
|
|
938
|
+
|
|
939
|
+
self.reticleView.opaque = NO;
|
|
940
|
+
self.reticleView.contentMode = UIViewContentModeScaleAspectFit;
|
|
941
|
+
self.reticleView.autoresizingMask = (UIViewAutoresizing) (0
|
|
942
|
+
| UIViewAutoresizingFlexibleLeftMargin
|
|
943
|
+
| UIViewAutoresizingFlexibleRightMargin
|
|
944
|
+
| UIViewAutoresizingFlexibleTopMargin
|
|
945
|
+
| UIViewAutoresizingFlexibleBottomMargin)
|
|
946
|
+
;
|
|
947
|
+
|
|
948
|
+
[overlayView addSubview: self.reticleView];
|
|
949
|
+
[self resizeElements];
|
|
950
|
+
return overlayView;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
//--------------------------------------------------------------------------
|
|
954
|
+
|
|
955
|
+
#define RETICLE_SIZE 500.0f
|
|
956
|
+
#define RETICLE_WIDTH 10.0f
|
|
957
|
+
#define RETICLE_OFFSET 60.0f
|
|
958
|
+
#define RETICLE_ALPHA 0.4f
|
|
959
|
+
|
|
960
|
+
//-------------------------------------------------------------------------
|
|
961
|
+
// builds the green box and red line
|
|
962
|
+
//-------------------------------------------------------------------------
|
|
963
|
+
- (UIImage*)buildReticleImage {
|
|
964
|
+
UIImage* result;
|
|
965
|
+
UIGraphicsBeginImageContext(CGSizeMake(RETICLE_SIZE, RETICLE_SIZE));
|
|
966
|
+
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
967
|
+
|
|
968
|
+
if (self.processor.is1D) {
|
|
969
|
+
UIColor* color = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:RETICLE_ALPHA];
|
|
970
|
+
CGContextSetStrokeColorWithColor(context, color.CGColor);
|
|
971
|
+
CGContextSetLineWidth(context, RETICLE_WIDTH);
|
|
972
|
+
CGContextBeginPath(context);
|
|
973
|
+
CGFloat lineOffset = (CGFloat) (RETICLE_OFFSET+(0.5*RETICLE_WIDTH));
|
|
974
|
+
CGContextMoveToPoint(context, lineOffset, RETICLE_SIZE/2);
|
|
975
|
+
CGContextAddLineToPoint(context, RETICLE_SIZE-lineOffset, (CGFloat) (0.5*RETICLE_SIZE));
|
|
976
|
+
CGContextStrokePath(context);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (self.processor.is2D) {
|
|
980
|
+
UIColor* color = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:RETICLE_ALPHA];
|
|
981
|
+
CGContextSetStrokeColorWithColor(context, color.CGColor);
|
|
982
|
+
CGContextSetLineWidth(context, RETICLE_WIDTH);
|
|
983
|
+
CGContextStrokeRect(context,
|
|
984
|
+
CGRectMake(
|
|
985
|
+
RETICLE_OFFSET,
|
|
986
|
+
RETICLE_OFFSET,
|
|
987
|
+
RETICLE_SIZE-2*RETICLE_OFFSET,
|
|
988
|
+
RETICLE_SIZE-2*RETICLE_OFFSET
|
|
989
|
+
)
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
result = UIGraphicsGetImageFromCurrentImageContext();
|
|
994
|
+
UIGraphicsEndImageContext();
|
|
995
|
+
return result;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
#pragma mark CDVBarcodeScannerOrientationDelegate
|
|
999
|
+
|
|
1000
|
+
- (BOOL)shouldAutorotate
|
|
1001
|
+
{
|
|
1002
|
+
return YES;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
|
|
1006
|
+
{
|
|
1007
|
+
return [[UIApplication sharedApplication] statusBarOrientation];
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
- (NSUInteger)supportedInterfaceOrientations
|
|
1011
|
+
{
|
|
1012
|
+
return UIInterfaceOrientationMaskAll;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
|
|
1016
|
+
{
|
|
1017
|
+
if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
|
|
1018
|
+
return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
return YES;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
|
|
1025
|
+
{
|
|
1026
|
+
[UIView setAnimationsEnabled:NO];
|
|
1027
|
+
AVCaptureVideoPreviewLayer* previewLayer = self.processor.previewLayer;
|
|
1028
|
+
previewLayer.frame = self.view.bounds;
|
|
1029
|
+
|
|
1030
|
+
if (orientation == UIInterfaceOrientationLandscapeLeft) {
|
|
1031
|
+
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft];
|
|
1032
|
+
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
|
|
1033
|
+
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeRight];
|
|
1034
|
+
} else if (orientation == UIInterfaceOrientationPortrait) {
|
|
1035
|
+
[previewLayer setOrientation:AVCaptureVideoOrientationPortrait];
|
|
1036
|
+
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
|
|
1037
|
+
[previewLayer setOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
|
1041
|
+
|
|
1042
|
+
[self resizeElements];
|
|
1043
|
+
[UIView setAnimationsEnabled:YES];
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
-(void) resizeElements {
|
|
1047
|
+
CGRect bounds = self.view.bounds;
|
|
1048
|
+
if (@available(iOS 11.0, *)) {
|
|
1049
|
+
bounds = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.width, self.view.safeAreaLayoutGuide.layoutFrame.size.height+self.view.safeAreaLayoutGuide.layoutFrame.origin.y);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
[self.toolbar sizeToFit];
|
|
1053
|
+
CGFloat toolbarHeight = [self.toolbar frame].size.height;
|
|
1054
|
+
CGFloat rootViewHeight = CGRectGetHeight(bounds);
|
|
1055
|
+
CGFloat rootViewWidth = CGRectGetWidth(bounds);
|
|
1056
|
+
CGRect rectArea = CGRectMake(0, rootViewHeight - toolbarHeight, rootViewWidth, toolbarHeight);
|
|
1057
|
+
[self.toolbar setFrame:rectArea];
|
|
1058
|
+
|
|
1059
|
+
CGFloat minAxis = MIN(rootViewHeight, rootViewWidth);
|
|
1060
|
+
|
|
1061
|
+
rectArea = CGRectMake(
|
|
1062
|
+
(CGFloat) (0.5 * (rootViewWidth - minAxis)),
|
|
1063
|
+
(CGFloat) (0.5 * (rootViewHeight - minAxis)),
|
|
1064
|
+
minAxis,
|
|
1065
|
+
minAxis
|
|
1066
|
+
);
|
|
1067
|
+
|
|
1068
|
+
[self.reticleView setFrame:rectArea];
|
|
1069
|
+
self.reticleView.center = CGPointMake(self.view.center.x, self.view.center.y-self.toolbar.frame.size.height/2);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
@end
|