// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/qr_scanner/ui_bundled/qr_scanner_camera_controller.h"
#import "base/apple/foundation_util.h"
@interface QRScannerCameraController () <AVCaptureMetadataOutputObjectsDelegate>
// The delegate which receives the QR scanned result.
@property(nonatomic, weak) id<QRScannerCameraControllerDelegate>
qrScannerDelegate;
@end
@implementation QRScannerCameraController
#pragma mark - Lifecycle
- (instancetype)initWithQRScannerDelegate:
(id<QRScannerCameraControllerDelegate>)qrScannerDelegate {
self = [super initWithDelegate:qrScannerDelegate];
if (self) {
_qrScannerDelegate = qrScannerDelegate;
}
return self;
}
#pragma mark - CameraController
- (void)configureScannerWithSession:(AVCaptureSession*)session {
// Configure metadata output.
AVCaptureMetadataOutput* metadataOutput =
[[AVCaptureMetadataOutput alloc] init];
[metadataOutput setMetadataObjectsDelegate:self
queue:dispatch_get_main_queue()];
if (![session canAddOutput:metadataOutput]) {
[self setCameraState:scanner::CAMERA_UNAVAILABLE];
return;
}
[session addOutput:metadataOutput];
NSArray* availableCodeTypes = [metadataOutput availableMetadataObjectTypes];
// Require QR code recognition to be available.
if (![availableCodeTypes containsObject:AVMetadataObjectTypeQRCode]) {
[self setCameraState:scanner::CAMERA_UNAVAILABLE];
return;
}
[metadataOutput setMetadataObjectTypes:availableCodeTypes];
self.metadataOutput = metadataOutput;
}
#pragma mark - AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput*)captureOutput
didOutputMetadataObjects:(NSArray*)metadataObjects
fromConnection:(AVCaptureConnection*)connection {
AVMetadataObject* metadataResult = [metadataObjects firstObject];
if (![metadataResult
isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
return;
}
NSString* resultString =
[base::apple::ObjCCastStrict<AVMetadataMachineReadableCodeObject>(
metadataResult) stringValue];
if (resultString.length == 0) {
return;
}
__weak CameraController* weakSelf = self;
dispatch_async(self.sessionQueue, ^{
CameraController* strongSelf = weakSelf;
if (strongSelf && [strongSelf.captureSession isRunning]) {
[strongSelf.captureSession stopRunning];
}
});
// Check if the barcode can only contain digits. In this case, the result can
// be loaded immediately.
NSString* resultType = metadataResult.type;
BOOL isAllDigits =
[resultType isEqualToString:AVMetadataObjectTypeUPCECode] ||
[resultType isEqualToString:AVMetadataObjectTypeEAN8Code] ||
[resultType isEqualToString:AVMetadataObjectTypeEAN13Code] ||
[resultType isEqualToString:AVMetadataObjectTypeInterleaved2of5Code] ||
[resultType isEqualToString:AVMetadataObjectTypeITF14Code];
// Note: `captureOutput` is called on the main queue. This is specified by
// `setMetadataObjectsDelegate:queue:`.
[self.qrScannerDelegate receiveQRScannerResult:resultString
loadImmediately:isAllDigits];
}
@end