UNPKG

9.68 kBPlain TextView Raw
1// Copyright 2018-present 650 Industries. All rights reserved.
2
3#import <EXBarCodeScanner/EXBarCodeScannerView.h>
4#import <EXBarCodeScanner/EXBarCodeScanner.h>
5#import <EXBarCodeScanner/EXBarCodeScannerUtils.h>
6#import <UMPermissionsInterface/UMPermissionsInterface.h>
7#import <UMCore/UMAppLifecycleService.h>
8#import <UMCore/UMUtilities.h>
9
10@interface EXBarCodeScannerView ()
11
12@property (nonatomic, strong) dispatch_queue_t sessionQueue;
13@property (nonatomic, strong) AVCaptureSession *session;
14@property (nonatomic, strong) AVCaptureDeviceInput *videoCaptureDeviceInput;
15@property (nonatomic, strong) AVCaptureMetadataOutput *metadataOutput;
16@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
17@property (nonatomic, strong) id runtimeErrorHandlingObserver;
18@property (nonatomic, strong) EXBarCodeScanner *barCodeScanner;
19
20@property (nonatomic, weak) UMModuleRegistry *moduleRegistry;
21@property (nonatomic, weak) id<UMPermissionsInterface> permissionsManager;
22@property (nonatomic, weak) id<UMAppLifecycleService> lifecycleManager;
23
24@property (nonatomic, assign, getter=isSessionPaused) BOOL paused;
25
26@property (nonatomic, copy) UMDirectEventBlock onCameraReady;
27@property (nonatomic, copy) UMDirectEventBlock onMountError;
28@property (nonatomic, copy) UMDirectEventBlock onBarCodeScanned;
29
30@end
31
32@implementation EXBarCodeScannerView
33
34- (instancetype)initWithModuleRegistry:(UMModuleRegistry *)moduleRegistry
35{
36 if ((self = [super init])) {
37 _presetCamera = AVCaptureDevicePositionBack;
38 _moduleRegistry = moduleRegistry;
39 _session = [AVCaptureSession new];
40 _sessionQueue = dispatch_queue_create("barCodeScannerQueue", DISPATCH_QUEUE_SERIAL);
41 _lifecycleManager = [moduleRegistry getModuleImplementingProtocol:@protocol(UMAppLifecycleListener)];
42 _permissionsManager = [moduleRegistry getModuleImplementingProtocol:@protocol(UMPermissionsInterface)];
43 _barCodeScanner = [self createBarCodeScanner];
44
45#if !(TARGET_IPHONE_SIMULATOR)
46 _previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
47 _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
48 _previewLayer.needsDisplayOnBoundsChange = YES;
49#endif
50 _paused = NO;
51
52 [_lifecycleManager registerAppLifecycleListener:self];
53 [[NSNotificationCenter defaultCenter] addObserver:self
54 selector:@selector(orientationChanged:)
55 name:UIDeviceOrientationDidChangeNotification
56 object:nil];
57
58 [self changePreviewOrientation:[UIApplication sharedApplication].statusBarOrientation];
59 [self initializeSession];
60 }
61 return self;
62}
63
64# pragma mark - events
65
66- (void)onReady
67{
68 if (_onCameraReady) {
69 _onCameraReady(nil);
70 }
71}
72
73- (void)onMountingError:(NSDictionary *)event
74{
75 if (_onMountError) {
76 _onMountError(event);
77 }
78}
79
80- (void)onBarCodeScanned:(NSDictionary *)event
81{
82 if (_onBarCodeScanned) {
83 _onBarCodeScanned(event);
84 }
85}
86
87# pragma mark - JS properties setters
88
89- (void)setPresetCamera:(NSInteger)presetCamera
90{
91 if (_presetCamera == presetCamera) {
92 return;
93 }
94 _presetCamera = presetCamera;
95 UM_WEAKIFY(self);
96 dispatch_async(_sessionQueue, ^{
97 UM_ENSURE_STRONGIFY(self);
98 [self initializeSession];
99 });
100}
101
102- (void)setBarCodeTypes:(NSArray *)barCodeTypes
103{
104 _barCodeTypes = barCodeTypes;
105 [_barCodeScanner setSettings:@{
106 @"barCodeTypes": barCodeTypes,
107 }];
108}
109
110# pragma mark - lifecycle
111
112- (void)layoutSubviews
113{
114 [super layoutSubviews];
115 _previewLayer.frame = self.bounds;
116 [self setBackgroundColor:[UIColor blackColor]];
117 [self.layer insertSublayer:_previewLayer atIndex:0];
118}
119
120- (void)removeFromSuperview
121{
122 [_lifecycleManager unregisterAppLifecycleListener:self];
123 [self stopSession];
124 [super removeFromSuperview];
125 [[NSNotificationCenter defaultCenter] removeObserver:self
126 name:UIDeviceOrientationDidChangeNotification
127 object:nil];
128}
129
130- (void)onAppForegrounded
131{
132 if (![_session isRunning] && [self isSessionPaused]) {
133 _paused = NO;
134 UM_WEAKIFY(self);
135 dispatch_async(_sessionQueue, ^{
136 UM_ENSURE_STRONGIFY(self);
137 [self.session startRunning];
138 });
139 }
140}
141
142- (void)onAppBackgrounded
143{
144 if ([_session isRunning] && ![self isSessionPaused]) {
145 _paused = YES;
146 UM_WEAKIFY(self);
147 dispatch_async(_sessionQueue, ^{
148 UM_ENSURE_STRONGIFY(self);
149 [self.session stopRunning];
150 });
151 }
152}
153
154# pragma mark - orientation
155
156- (void)orientationChanged:(NSNotification *)notification
157{
158 UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
159 [self changePreviewOrientation:orientation];
160}
161
162- (void)changePreviewOrientation:(UIInterfaceOrientation)orientation
163{
164 UM_WEAKIFY(self);
165 AVCaptureVideoOrientation videoOrientation = [EXBarCodeScannerUtils videoOrientationForInterfaceOrientation:orientation];
166 [UMUtilities performSynchronouslyOnMainThread:^{
167 UM_ENSURE_STRONGIFY(self);
168 if (self.previewLayer.connection.isVideoOrientationSupported) {
169 [self.previewLayer.connection setVideoOrientation:videoOrientation];
170 }
171 }];
172}
173
174# pragma mark - session
175
176- (BOOL)ensurePermissionsGranted
177{
178 NSDictionary *cameraPermissions = [_permissionsManager getPermissionsForResource:@"camera"];
179 if (![cameraPermissions[@"status"] isEqualToString:@"granted"]) {
180 [self onMountingError:@{@"message": @"Camera permissions not granted - component could not be rendered."}];
181 return FALSE;
182 }
183 return TRUE;
184}
185
186- (void)initializeSession
187{
188 if (_videoCaptureDeviceInput.device.position == _presetCamera) {
189 return;
190 }
191
192 __block UIInterfaceOrientation interfaceOrientation;
193 [UMUtilities performSynchronouslyOnMainThread:^{
194 interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
195 }];
196 AVCaptureVideoOrientation orientation = [EXBarCodeScannerUtils videoOrientationForInterfaceOrientation:interfaceOrientation];
197
198 UM_WEAKIFY(self);
199 dispatch_async(_sessionQueue, ^{
200 UM_ENSURE_STRONGIFY(self);
201
202 [self.session beginConfiguration];
203
204 NSError *error = nil;
205 AVCaptureDevice *captureDevice = [EXBarCodeScannerUtils deviceWithMediaType:AVMediaTypeVideo
206 preferringPosition:self.presetCamera];
207 AVCaptureDeviceInput *captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
208
209 if (error || captureDeviceInput == nil) {
210 NSString *errorMessage = @"Camera could not be started - ";
211 if (error) {
212 errorMessage = [errorMessage stringByAppendingString:[error description]];
213 } else {
214 errorMessage = [errorMessage stringByAppendingString:@"there's no captureDeviceInput available"];
215 }
216 [self onMountingError:@{@"message": errorMessage}];
217 return;
218 }
219
220 [self.session removeInput:self.videoCaptureDeviceInput];
221 if ([self.session canAddInput:captureDeviceInput]) {
222 [self.session addInput:captureDeviceInput];
223
224 self.videoCaptureDeviceInput = captureDeviceInput;
225 [self.previewLayer.connection setVideoOrientation:orientation];
226 }
227
228 [self.session commitConfiguration];
229 if (!self.session.isRunning) {
230 [self startSession];
231 }
232 });
233}
234
235- (void)startSession
236{
237#pragma clang diagnostic push
238#pragma clang diagnostic ignored "-Wunreachable-code"
239#if TARGET_IPHONE_SIMULATOR
240 return;
241#endif
242 if (![self ensurePermissionsGranted]) {
243 return;
244 };
245
246 UM_WEAKIFY(self);
247 dispatch_async(_sessionQueue, ^{
248 UM_ENSURE_STRONGIFY(self);
249
250 if (self.presetCamera == AVCaptureDevicePositionUnspecified) {
251 return;
252 }
253
254 [self setRuntimeErrorHandlingObserver:
255 [[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureSessionRuntimeErrorNotification
256 object:self.session
257 queue:nil
258 usingBlock:^(NSNotification *note) {
259 UM_ENSURE_STRONGIFY(self);
260 dispatch_async(self.sessionQueue, ^{
261 UM_ENSURE_STRONGIFY(self);
262 // Manually restarting the session since it must have been stopped due to an error.
263 [self.session startRunning];
264 [self onReady];
265 });
266 }]];
267
268 [self.barCodeScanner maybeStartBarCodeScanning];
269
270 [self.session startRunning];
271 [self onReady];
272 });
273#pragma clang diagnostic pop
274}
275
276- (void)stopSession
277{
278#if TARGET_IPHONE_SIMULATOR
279 return;
280#endif
281 UM_WEAKIFY(self);
282 dispatch_async(_sessionQueue, ^{
283 UM_ENSURE_STRONGIFY(self);
284
285 [self.barCodeScanner stopBarCodeScanning];
286
287 [self.previewLayer removeFromSuperlayer];
288 [self.session commitConfiguration];
289 [self.session stopRunning];
290 for (AVCaptureInput *input in self.session.inputs) {
291 [self.session removeInput:input];
292 }
293
294 for (AVCaptureOutput *output in self.session.outputs) {
295 [self.session removeOutput:output];
296 }
297 });
298}
299
300# pragma mark - BarCode scanner
301
302- (EXBarCodeScanner *)createBarCodeScanner
303{
304 EXBarCodeScanner *barCodeScanner = [EXBarCodeScanner new];
305 [barCodeScanner setSession:_session];
306 [barCodeScanner setSessionQueue:_sessionQueue];
307 UM_WEAKIFY(self);
308 [barCodeScanner setOnBarCodeScanned:^(NSDictionary *body) {
309 UM_ENSURE_STRONGIFY(self);
310 [self onBarCodeScanned:body];
311 }];
312 [barCodeScanner setIsEnabled:true];
313 return barCodeScanner;
314}
315
316@end