Commit 18f1f26a authored by Tanisha Mandre's avatar Tanisha Mandre Committed by Commit Bot

Refactor QR scanner camera controller to subclass a QR scanner camera controller.

Aim: Remove the QR Scanner Specific methods out of the camera controller so that a similar subclass of the generic camera controller super class can be implemented for the credit card scanner.

Architecture:
Superclass - cameraController
Subclasses - qrScannerCameraController

Methods shifted:

1. - (void)continueLoadCaptureSession:(AVCaptureVideoPreviewLayer*)previewLayer;
Modifies the method to move the section that configures the QR scanner metadata output to a separate method 'configureScannerWithSession', implemented in the QR Scanner Camera Controller.

2. - (void)captureOutput:(AVCaptureOutput*)captureOutput
    didOutputMetadataObjects:(NSArray*)metadataObjects
              fromConnection:(AVCaptureConnection*)connection;
Moves this method to the QR Scanner Camera Controller. It reads and processes the scanned QR code data.

Bug: 990744

Change-Id: If07340c62b0c5c11c70a1e6c4d0a6782dd2c774e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1754008
Commit-Queue: Tanisha Mandre <tanishamandre@google.com>
Reviewed-by: default avatarJavier Ernesto Flores Robles <javierrobles@chromium.org>
Reviewed-by: default avatarRohit Rao <rohitrao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#688503}
parent 2f34373f
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
source_set("qr_scanner") { source_set("qr_scanner") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
"qr_scanner_camera_controller.h",
"qr_scanner_camera_controller.mm",
"qr_scanner_camera_controller_delegate.h",
"qr_scanner_view.h", "qr_scanner_view.h",
"qr_scanner_view.mm", "qr_scanner_view.mm",
"qr_scanner_view_controller.h", "qr_scanner_view_controller.h",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_CAMERA_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_CAMERA_CONTROLLER_H_
#import "ios/chrome/browser/ui/qr_scanner/qr_scanner_camera_controller_delegate.h"
// The QRScannerCameraController manages the AVCaptureSession, its inputs,
// outputs, and notifications for the QRScannerViewController.
@interface QRScannerCameraController : CameraController
// Initializes the QR scanner camera controller with the camera controller
// delegate and the QR scanner delegate.
- (instancetype)initWithCameraControllerDelegate:
(id<CameraControllerDelegate>)cameraControllerDelegate
qrScannerDelegate:
(id<QRScannerCameraControllerDelegate>)
qrScannerDelegate
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithDelegate:(id<CameraControllerDelegate>)delegate
NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_CAMERA_CONTROLLER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/qr_scanner/qr_scanner_camera_controller.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface QRScannerCameraController () <AVCaptureMetadataOutputObjectsDelegate>
// The delegate which receives the QR scanned result.
@property(nonatomic, weak) id<QRScannerCameraControllerDelegate>
qrScannerDelegate;
@end
@implementation QRScannerCameraController
#pragma mark - Lifecycle
- (instancetype)initWithCameraControllerDelegate:
(id<CameraControllerDelegate>)cameraControllerDelegate
qrScannerDelegate:
(id<QRScannerCameraControllerDelegate>)
qrScannerDelegate {
self = [super initWithDelegate:cameraControllerDelegate];
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::mac::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
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_CAMERA_CONTROLLER_DELEGATE_H_
#define IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_CAMERA_CONTROLLER_DELEGATE_H_
#import "ios/chrome/browser/ui/scanner/camera_controller.h"
// Receives the QR scanner results.
@protocol QRScannerCameraControllerDelegate <CameraControllerDelegate>
// Called when the scanner detects a valid code. The camera controller stops
// recording when a result is scanned. A valid code is any non-empty string. If
// |load| is YES, the result should be loaded immediately without requiring
// additional user input. The value of |load| will only be YES for barcodes
// which can only encode digits.
- (void)receiveQRScannerResult:(NSString*)result loadImmediately:(BOOL)load;
@end
#endif // IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_CAMERA_CONTROLLER_DELEGATE_H_
...@@ -5,12 +5,14 @@ ...@@ -5,12 +5,14 @@
#ifndef IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_VIEW_CONTROLLER_H_ #ifndef IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_VIEW_CONTROLLER_H_ #define IOS_CHROME_BROWSER_UI_QR_SCANNER_QR_SCANNER_VIEW_CONTROLLER_H_
#include "ios/chrome/browser/ui/qr_scanner/qr_scanner_camera_controller.h"
#import "ios/chrome/browser/ui/scanner/scanner_view_controller.h" #import "ios/chrome/browser/ui/scanner/scanner_view_controller.h"
@protocol LoadQueryCommands; @protocol LoadQueryCommands;
// View controller for the QR Scanner. // View controller for the QR Scanner.
@interface QRScannerViewController : ScannerViewController @interface QRScannerViewController
: ScannerViewController <QRScannerCameraControllerDelegate>
- (instancetype)initWithPresentationProvider: - (instancetype)initWithPresentationProvider:
(id<ScannerPresenting>)presentationProvider (id<ScannerPresenting>)presentationProvider
...@@ -18,9 +20,7 @@ ...@@ -18,9 +20,7 @@
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithPresentationProvider: - (instancetype)initWithPresentationProvider:
(id<ScannerPresenting>)presentationProvider (id<ScannerPresenting>)presentationProvider NS_UNAVAILABLE;
cameraController:(CameraController*)cameraController
NS_UNAVAILABLE;
- (instancetype)initWithNibName:(NSString*)name - (instancetype)initWithNibName:(NSString*)name
bundle:(NSBundle*)bundle NS_UNAVAILABLE; bundle:(NSBundle*)bundle NS_UNAVAILABLE;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h" #include "base/metrics/user_metrics_action.h"
#include "ios/chrome/browser/ui/commands/load_query_commands.h" #include "ios/chrome/browser/ui/commands/load_query_commands.h"
#import "ios/chrome/browser/ui/qr_scanner/qr_scanner_camera_controller.h"
#include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h" #include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h"
#include "ios/chrome/browser/ui/scanner/scanner_alerts.h" #include "ios/chrome/browser/ui/scanner/scanner_alerts.h"
#include "ios/chrome/browser/ui/scanner/scanner_presenting.h" #include "ios/chrome/browser/ui/scanner/scanner_presenting.h"
...@@ -43,10 +44,7 @@ using base::UserMetricsAction; ...@@ -43,10 +44,7 @@ using base::UserMetricsAction;
- (instancetype) - (instancetype)
initWithPresentationProvider:(id<ScannerPresenting>)presentationProvider initWithPresentationProvider:(id<ScannerPresenting>)presentationProvider
queryLoader:(id<LoadQueryCommands>)queryLoader { queryLoader:(id<LoadQueryCommands>)queryLoader {
self = [super self = [super initWithPresentationProvider:presentationProvider];
initWithPresentationProvider:presentationProvider
cameraController:[CameraController
cameraControllerWithDelegate:self]];
if (self) { if (self) {
_queryLoader = queryLoader; _queryLoader = queryLoader;
} }
...@@ -59,6 +57,12 @@ using base::UserMetricsAction; ...@@ -59,6 +57,12 @@ using base::UserMetricsAction;
return [[QRScannerView alloc] initWithFrame:self.view.frame delegate:self]; return [[QRScannerView alloc] initWithFrame:self.view.frame delegate:self];
} }
- (CameraController*)buildCameraController {
return
[[QRScannerCameraController alloc] initWithCameraControllerDelegate:self
qrScannerDelegate:self];
}
- (void)dismissForReason:(scannerViewController::DismissalReason)reason - (void)dismissForReason:(scannerViewController::DismissalReason)reason
withCompletion:(void (^)(void))completion { withCompletion:(void (^)(void))completion {
switch (reason) { switch (reason) {
...@@ -78,4 +82,27 @@ using base::UserMetricsAction; ...@@ -78,4 +82,27 @@ using base::UserMetricsAction;
[super dismissForReason:reason withCompletion:completion]; [super dismissForReason:reason withCompletion:completion];
} }
#pragma mark - QRScannerCameraControllerDelegate
- (void)receiveQRScannerResult:(NSString*)result loadImmediately:(BOOL)load {
if (UIAccessibilityIsVoiceOverRunning()) {
// Post a notification announcing that a code was scanned. QR scanner will
// be dismissed when the UIAccessibilityAnnouncementDidFinishNotification is
// received.
_result = [result copy];
_loadResultImmediately = load;
UIAccessibilityPostNotification(
UIAccessibilityAnnouncementNotification,
l10n_util::GetNSString(
IDS_IOS_SCANNER_SCANNED_ACCESSIBILITY_ANNOUNCEMENT));
} else {
[self.scannerView animateScanningResultWithCompletion:^void(void) {
[self dismissForReason:scannerViewController::SCANNED_CODE
withCompletion:^{
[self.queryLoader loadQuery:result immediately:load];
}];
}];
}
}
@end @end
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#import "ios/chrome/browser/ui/location_bar/location_bar_coordinator.h" #import "ios/chrome/browser/ui/location_bar/location_bar_coordinator.h"
#import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h" #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
#include "ios/chrome/browser/ui/omnibox/location_bar_delegate.h" #include "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
#include "ios/chrome/browser/ui/qr_scanner/qr_scanner_camera_controller.h"
#include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h" #include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h"
#include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h" #include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h"
#include "ios/chrome/browser/ui/scanner/camera_controller.h" #include "ios/chrome/browser/ui/scanner/camera_controller.h"
...@@ -394,16 +395,17 @@ void TapKeyboardReturnKeyInOmniboxWithText(std::string text) { ...@@ -394,16 +395,17 @@ void TapKeyboardReturnKeyInOmniboxWithText(std::string text) {
#pragma mark - #pragma mark -
#pragma mark Helpers for mocks #pragma mark Helpers for mocks
// Swizzles the CameraController method cameraControllerWithDelegate: to return // Swizzles the QRScannerViewController property cameraController: to return
// |cameraControllerMock| instead of a new instance of CameraController. // |cameraControllerMock| instead of a new instance of CameraController.
- (void)swizzleCameraController:(id)cameraControllerMock { - (void)swizzleCameraController:(id)cameraControllerMock {
CameraController* (^swizzleCameraControllerBlock)( QRScannerCameraController* (^swizzleCameraControllerBlock)(
id<CameraControllerDelegate>) = ^(id<CameraControllerDelegate> delegate) { id<QRScannerCameraControllerDelegate>) =
return cameraControllerMock; ^(id<QRScannerCameraControllerDelegate> delegate) {
}; return cameraControllerMock;
};
camera_controller_swizzler_.reset(new ScopedBlockSwizzler( camera_controller_swizzler_.reset(new ScopedBlockSwizzler(
[CameraController class], @selector(cameraControllerWithDelegate:), [QRScannerViewController class], @selector(cameraController),
swizzleCameraControllerBlock)); swizzleCameraControllerBlock));
} }
...@@ -435,7 +437,7 @@ void TapKeyboardReturnKeyInOmniboxWithText(std::string text) { ...@@ -435,7 +437,7 @@ void TapKeyboardReturnKeyInOmniboxWithText(std::string text) {
// |granted| is set to YES. // |granted| is set to YES.
- (id)getCameraControllerMockWithAuthorizationStatus: - (id)getCameraControllerMockWithAuthorizationStatus:
(AVAuthorizationStatus)authorizationStatus { (AVAuthorizationStatus)authorizationStatus {
id mock = [OCMockObject mockForClass:[CameraController class]]; id mock = [OCMockObject mockForClass:[QRScannerCameraController class]];
[[[mock stub] andReturnValue:OCMOCK_VALUE(authorizationStatus)] [[[mock stub] andReturnValue:OCMOCK_VALUE(authorizationStatus)]
getAuthorizationStatus]; getAuthorizationStatus];
return mock; return mock;
......
...@@ -51,27 +51,21 @@ enum CameraState { ...@@ -51,27 +51,21 @@ enum CameraState {
- (void)torchStateChanged:(BOOL)torchIsOn; - (void)torchStateChanged:(BOOL)torchIsOn;
// Called on the main queue when the torch availability changes. // Called on the main queue when the torch availability changes.
- (void)torchAvailabilityChanged:(BOOL)torchIsAvailable; - (void)torchAvailabilityChanged:(BOOL)torchIsAvailable;
// Called when the scanner detects a valid code. The camera controller stops
// recording when a result is scanned. A valid code is any non-empty string. If
// |load| is YES, the result should be loaded immediately without requiring
// additional user input. The value of |load| will only be YES for barcodes
// which can only encode digits.
- (void)receiveQRScannerResult:(NSString*)result loadImmediately:(BOOL)load;
@end @end
// The CameraController manages the AVCaptureSession, its inputs, outputs, and // The CameraController manages the AVCaptureSession, its inputs, outputs, and
// notifications for the QRScannerViewController. // notifications for the ScannerViewController.
@interface CameraController : NSObject @interface CameraController : NSObject
// The current state of the torch. // The current state of the torch.
@property(nonatomic, readonly, assign, getter=isTorchActive) BOOL torchActive; @property(nonatomic, readonly, assign, getter=isTorchActive) BOOL torchActive;
- (instancetype)init NS_UNAVAILABLE; // Initializes the controller with the |delegate|.
- (instancetype)initWithDelegate:(id<CameraControllerDelegate>)delegate
NS_DESIGNATED_INITIALIZER;
// Returns a new controller with the |delegate|. - (instancetype)init NS_UNAVAILABLE;
+ (instancetype)cameraControllerWithDelegate:
(id<CameraControllerDelegate>)delegate;
// Returns the app's authorization in regards to the camera. // Returns the app's authorization in regards to the camera.
- (AVAuthorizationStatus)getAuthorizationStatus; - (AVAuthorizationStatus)getAuthorizationStatus;
...@@ -109,4 +103,25 @@ enum CameraState { ...@@ -109,4 +103,25 @@ enum CameraState {
@end @end
@interface CameraController (Subclassing)
// The queue for dispatching calls to |_captureSession|.
@property(nonatomic, readonly) dispatch_queue_t sessionQueue;
// The capture session for recording video and detecting QR codes or credit
// cards.
@property(nonatomic, readwrite) AVCaptureSession* captureSession;
// The metadata output attached to the capture session.
@property(nonatomic, readwrite) AVCaptureMetadataOutput* metadataOutput;
// Set camera state.
- (void)setCameraState:(scanner::CameraState)state;
// Configures the scanner specific capture session elements, i.e. either QR code
// or credit card scanner. Must be overridden in the subclass.
- (void)configureScannerWithSession:(AVCaptureSession*)session;
@end
#endif // IOS_CHROME_BROWSER_UI_SCANNER_CAMERA_CONTROLLER_H_ #endif // IOS_CHROME_BROWSER_UI_SCANNER_CAMERA_CONTROLLER_H_
...@@ -13,11 +13,14 @@ ...@@ -13,11 +13,14 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
@interface CameraController () <AVCaptureMetadataOutputObjectsDelegate> { @interface CameraController ()
// The queue for dispatching calls to |_captureSession|.
dispatch_queue_t _sessionQueue;
}
// The queue for dispatching calls to |_captureSession|.
@property(nonatomic, readonly) dispatch_queue_t sessionQueue;
// The capture session for recording video and detecting QR codes.
@property(nonatomic, readwrite) AVCaptureSession* captureSession;
// The metadata output attached to the capture session.
@property(nonatomic, readwrite) AVCaptureMetadataOutput* metadataOutput;
// The delegate which receives the scanned result. All methods of this // The delegate which receives the scanned result. All methods of this
// delegate should be called on the main queue. // delegate should be called on the main queue.
@property(nonatomic, readwrite, weak) id<CameraControllerDelegate> delegate; @property(nonatomic, readwrite, weak) id<CameraControllerDelegate> delegate;
...@@ -32,16 +35,8 @@ ...@@ -32,16 +35,8 @@
// The state of KVO for the camera. Used to stop observing on dealloc. // The state of KVO for the camera. Used to stop observing on dealloc.
@property(nonatomic, readwrite, assign, getter=isObservingCamera) @property(nonatomic, readwrite, assign, getter=isObservingCamera)
BOOL observingCamera; BOOL observingCamera;
// The capture session for recording video and detecting QR codes.
@property(nonatomic, readwrite) AVCaptureSession* captureSession;
// The metadata output attached to the capture session.
@property(nonatomic, readwrite) AVCaptureMetadataOutput* metadataOutput;
@property(nonatomic, readwrite, assign) CGRect viewportRect; @property(nonatomic, readwrite, assign) CGRect viewportRect;
// Initializes the controller with the |delegate|.
- (instancetype)initWithDelegate:(id<CameraControllerDelegate>)delegate
NS_DESIGNATED_INITIALIZER;
// YES if |cameraState| is CAMERA_AVAILABLE. // YES if |cameraState| is CAMERA_AVAILABLE.
- (BOOL)isCameraAvailable; - (BOOL)isCameraAvailable;
// Starts receiving notfications about changes to the capture session and to the // Starts receiving notfications about changes to the capture session and to the
...@@ -61,13 +56,6 @@ ...@@ -61,13 +56,6 @@
#pragma mark - Lifecycle #pragma mark - Lifecycle
+ (instancetype)cameraControllerWithDelegate:
(id<CameraControllerDelegate>)delegate {
CameraController* cameraController =
[[CameraController alloc] initWithDelegate:delegate];
return cameraController;
}
- (instancetype)initWithDelegate:(id<CameraControllerDelegate>)delegate { - (instancetype)initWithDelegate:(id<CameraControllerDelegate>)delegate {
self = [super init]; self = [super init];
if (self) { if (self) {
...@@ -245,25 +233,7 @@ ...@@ -245,25 +233,7 @@
} }
[session addInput:videoInput]; [session addInput:videoInput];
// Configure metadata output. [self configureScannerWithSession:session];
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];
_metadataOutput = metadataOutput;
_captureSession = session; _captureSession = session;
[self setCameraState:scanner::CAMERA_AVAILABLE]; [self setCameraState:scanner::CAMERA_AVAILABLE];
...@@ -288,6 +258,10 @@ ...@@ -288,6 +258,10 @@
[self startRecording]; [self startRecording];
} }
- (void)configureScannerWithSession:(AVCaptureSession*)session {
NOTREACHED();
}
- (void)startReceivingNotifications { - (void)startReceivingNotifications {
// Start receiving notifications about changes to the capture session. // Start receiving notifications about changes to the capture session.
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
...@@ -466,43 +440,4 @@ ...@@ -466,43 +440,4 @@
}); });
} }
#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::mac::ObjCCastStrict<AVMetadataMachineReadableCodeObject>(
metadataResult) stringValue];
if (resultString.length == 0) {
return;
}
__weak CameraController* weakSelf = self;
dispatch_async(_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:|.
[_delegate receiveQRScannerResult:resultString loadImmediately:isAllDigits];
}
@end @end
...@@ -33,14 +33,16 @@ enum DismissalReason { ...@@ -33,14 +33,16 @@ enum DismissalReason {
// Stores the camera controller for the scanner. // Stores the camera controller for the scanner.
@property(nonatomic, readwrite, strong) CameraController* cameraController; @property(nonatomic, readwrite, strong) CameraController* cameraController;
// Stores the view for the scanner. Can be subclassed as a QR code or Credit
// Card scanner view.
@property(nonatomic, readwrite) ScannerView* scannerView;
// Stores the presentation provider. // Stores the presentation provider.
@property(nonatomic, readwrite, weak) id<ScannerPresenting> @property(nonatomic, readwrite, weak) id<ScannerPresenting>
presentationProvider; presentationProvider;
- (instancetype)initWithPresentationProvider: - (instancetype)initWithPresentationProvider:
(id<ScannerPresenting>)presentationProvider (id<ScannerPresenting>)presentationProvider NS_DESIGNATED_INITIALIZER;
cameraController:(CameraController*)cameraController
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithNibName:(NSString*)name - (instancetype)initWithNibName:(NSString*)name
bundle:(NSBundle*)bundle NS_UNAVAILABLE; bundle:(NSBundle*)bundle NS_UNAVAILABLE;
...@@ -55,6 +57,9 @@ enum DismissalReason { ...@@ -55,6 +57,9 @@ enum DismissalReason {
// Builds the scanner view. Must be overridden in the subclass. // Builds the scanner view. Must be overridden in the subclass.
- (ScannerView*)buildScannerView; - (ScannerView*)buildScannerView;
// Builds the camera controller. Must be overridden in the subclass.
- (CameraController*)buildCameraController;
// Dismiss scanner. Subclass can override to update metrics. // Dismiss scanner. Subclass can override to update metrics.
// implementation. // implementation.
- (void)dismissForReason:(scannerViewController::DismissalReason)reason - (void)dismissForReason:(scannerViewController::DismissalReason)reason
......
...@@ -34,23 +34,17 @@ using base::UserMetricsAction; ...@@ -34,23 +34,17 @@ using base::UserMetricsAction;
@property(nonatomic, readwrite, weak) id<LoadQueryCommands> queryLoader; @property(nonatomic, readwrite, weak) id<LoadQueryCommands> queryLoader;
// Stores the view for the scanner. Can be subclassed as a QR code or Credit
// Card scanner view.
@property(nonatomic, readwrite) ScannerView* scannerView;
@end @end
@implementation ScannerViewController @implementation ScannerViewController
#pragma mark - lifecycle #pragma mark - lifecycle
- (instancetype) - (instancetype)initWithPresentationProvider:
initWithPresentationProvider:(id<ScannerPresenting>)presentationProvider (id<ScannerPresenting>)presentationProvider {
cameraController:(CameraController*)cameraController {
self = [super initWithNibName:nil bundle:nil]; self = [super initWithNibName:nil bundle:nil];
if (self) { if (self) {
_presentationProvider = presentationProvider; _presentationProvider = presentationProvider;
_cameraController = cameraController;
} }
return self; return self;
} }
...@@ -67,7 +61,7 @@ using base::UserMetricsAction; ...@@ -67,7 +61,7 @@ using base::UserMetricsAction;
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
DCHECK(_cameraController); DCHECK(self.cameraController);
[self.view addSubview:self.scannerView]; [self.view addSubview:self.scannerView];
...@@ -85,13 +79,13 @@ using base::UserMetricsAction; ...@@ -85,13 +79,13 @@ using base::UserMetricsAction;
]]; ]];
AVCaptureVideoPreviewLayer* previewLayer = [self.scannerView getPreviewLayer]; AVCaptureVideoPreviewLayer* previewLayer = [self.scannerView getPreviewLayer];
switch ([_cameraController getAuthorizationStatus]) { switch ([self.cameraController getAuthorizationStatus]) {
case AVAuthorizationStatusNotDetermined: case AVAuthorizationStatusNotDetermined:
[_cameraController [self.cameraController
requestAuthorizationAndLoadCaptureSession:previewLayer]; requestAuthorizationAndLoadCaptureSession:previewLayer];
break; break;
case AVAuthorizationStatusAuthorized: case AVAuthorizationStatusAuthorized:
[_cameraController loadCaptureSession:previewLayer]; [self.cameraController loadCaptureSession:previewLayer];
break; break;
case AVAuthorizationStatusRestricted: case AVAuthorizationStatusRestricted:
case AVAuthorizationStatusDenied: case AVAuthorizationStatusDenied:
...@@ -108,7 +102,7 @@ using base::UserMetricsAction; ...@@ -108,7 +102,7 @@ using base::UserMetricsAction;
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
[self startReceivingNotifications]; [self startReceivingNotifications];
[_cameraController startRecording]; [self.cameraController startRecording];
// Reset torch. // Reset torch.
[self setTorchMode:AVCaptureTorchModeOff]; [self setTorchMode:AVCaptureTorchModeOff];
...@@ -144,14 +138,14 @@ using base::UserMetricsAction; ...@@ -144,14 +138,14 @@ using base::UserMetricsAction;
// Reset the size of the preview if the bounds of the view controller // Reset the size of the preview if the bounds of the view controller
// changed. This can happen if entering or leaving Split View mode on iPad. // changed. This can happen if entering or leaving Split View mode on iPad.
[self.scannerView resetPreviewFrame:size]; [self.scannerView resetPreviewFrame:size];
[_cameraController [self.cameraController
resetVideoOrientation:[self.scannerView getPreviewLayer]]; resetVideoOrientation:[self.scannerView getPreviewLayer]];
} }
} }
- (void)viewDidDisappear:(BOOL)animated { - (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated]; [super viewDidDisappear:animated];
[_cameraController stopRecording]; [self.cameraController stopRecording];
[self stopReceivingNotifications]; [self stopReceivingNotifications];
// Reset torch. // Reset torch.
...@@ -165,8 +159,8 @@ using base::UserMetricsAction; ...@@ -165,8 +159,8 @@ using base::UserMetricsAction;
#pragma mark - public methods #pragma mark - public methods
- (UIViewController*)getViewControllerToPresent { - (UIViewController*)getViewControllerToPresent {
DCHECK(_cameraController); DCHECK(self.cameraController);
switch ([_cameraController getAuthorizationStatus]) { switch ([self.cameraController getAuthorizationStatus]) {
case AVAuthorizationStatusNotDetermined: case AVAuthorizationStatusNotDetermined:
case AVAuthorizationStatusAuthorized: case AVAuthorizationStatusAuthorized:
_transitioningDelegate = [[ScannerTransitioningDelegate alloc] init]; _transitioningDelegate = [[ScannerTransitioningDelegate alloc] init];
...@@ -190,6 +184,11 @@ using base::UserMetricsAction; ...@@ -190,6 +184,11 @@ using base::UserMetricsAction;
return nil; return nil;
} }
- (CameraController*)buildCameraController {
NOTIMPLEMENTED();
return nil;
}
#pragma mark - private methods #pragma mark - private methods
// Starts receiving notifications about the UIApplication going to background. // Starts receiving notifications about the UIApplication going to background.
...@@ -213,10 +212,10 @@ using base::UserMetricsAction; ...@@ -213,10 +212,10 @@ using base::UserMetricsAction;
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
} }
// Requests the torch mode to be set to |mode| by the |_cameraController| // Requests the torch mode to be set to |mode| by the |self.cameraController|
// and the icon of the torch button to be changed by the |self.scannerView|. // and the icon of the torch button to be changed by the |self.scannerView|.
- (void)setTorchMode:(AVCaptureTorchMode)mode { - (void)setTorchMode:(AVCaptureTorchMode)mode {
[_cameraController setTorchMode:mode]; [self.cameraController setTorchMode:mode];
} }
- (ScannerView*)scannerView { - (ScannerView*)scannerView {
...@@ -226,6 +225,13 @@ using base::UserMetricsAction; ...@@ -226,6 +225,13 @@ using base::UserMetricsAction;
return _scannerView; return _scannerView;
} }
- (CameraController*)cameraController {
if (!_cameraController) {
_cameraController = [self buildCameraController];
}
return _cameraController;
}
#pragma mark - notification handlers #pragma mark - notification handlers
- (void)handleUIApplicationWillResignActiveNotification { - (void)handleUIApplicationWillResignActiveNotification {
...@@ -252,7 +258,7 @@ using base::UserMetricsAction; ...@@ -252,7 +258,7 @@ using base::UserMetricsAction;
#pragma mark - CameraControllerDelegate #pragma mark - CameraControllerDelegate
- (void)captureSessionIsConnected { - (void)captureSessionIsConnected {
[_cameraController setViewport:[self.scannerView viewportRectOfInterest]]; [self.cameraController setViewport:[self.scannerView viewportRectOfInterest]];
} }
- (void)cameraStateChanged:(scanner::CameraState)state { - (void)cameraStateChanged:(scanner::CameraState)state {
...@@ -297,27 +303,6 @@ using base::UserMetricsAction; ...@@ -297,27 +303,6 @@ using base::UserMetricsAction;
[self.scannerView enableTorchButton:torchIsAvailable]; [self.scannerView enableTorchButton:torchIsAvailable];
} }
- (void)receiveQRScannerResult:(NSString*)result loadImmediately:(BOOL)load {
if (UIAccessibilityIsVoiceOverRunning()) {
// Post a notification announcing that a code was scanned. QR scanner will
// be dismissed when the UIAccessibilityAnnouncementDidFinishNotification is
// received.
_result = [result copy];
_loadResultImmediately = load;
UIAccessibilityPostNotification(
UIAccessibilityAnnouncementNotification,
l10n_util::GetNSString(
IDS_IOS_SCANNER_SCANNED_ACCESSIBILITY_ANNOUNCEMENT));
} else {
[self.scannerView animateScanningResultWithCompletion:^void(void) {
[self dismissForReason:scannerViewController::SCANNED_CODE
withCompletion:^{
[self.queryLoader loadQuery:result immediately:load];
}];
}];
}
}
#pragma mark - ScannerViewDelegate #pragma mark - ScannerViewDelegate
- (void)dismissScannerView:(id)sender { - (void)dismissScannerView:(id)sender {
...@@ -326,7 +311,7 @@ using base::UserMetricsAction; ...@@ -326,7 +311,7 @@ using base::UserMetricsAction;
} }
- (void)toggleTorch:(id)sender { - (void)toggleTorch:(id)sender {
if ([_cameraController isTorchActive]) { if ([self.cameraController isTorchActive]) {
[self setTorchMode:AVCaptureTorchModeOff]; [self setTorchMode:AVCaptureTorchModeOff];
} else { } else {
base::RecordAction(UserMetricsAction("MobileQRScannerTorchOn")); base::RecordAction(UserMetricsAction("MobileQRScannerTorchOn"));
......
Markdown is supported
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