The QR Scanner provides a way of scanning QR codes and bar codes directly from Chrome. It is developed behind the EnableQRCodeReader experimental flag.
QRScannerViewControllerDelegate protocol.QRScannerViewController with this delegate.getViewControllerToPresent.The QR Scanner is presented as a full-screen view controller displaying a video preview, a control for the camera's torch, and a control for closing the QR scanner.
The QR Scanner is presented using a custom transition animation which makes it appear to be originally positioned under the presenting view controller.
The QR scanner can be accessed from the 3D Touch application shortcuts on supported devices. The SpotlightActions experiment allows the QR scanner to be accessed from Spotlight search. More info about Spotlight actions can be found at go/chrome-ios-spotlight and crbug.com/608733. Planned and rejected entry points are described in the design doc at go/chrome-ios-qr-code.
Tests for QR Scanner are a part of ios_chrome_ui_egtests.
CameraController and the QRScannerView and is responsible for displaying alerts from QRScannerAlerts.AVCaptureSession for the camera. It is responsible for loading the camera, listening for camera notifications, receiving the scanned result and informing the QRScannerViewController about changes to the state of the camera or the torch.Operations performed by CameraController are done on a separate dispatch queue, as recommended by the documentation for AVCaptureSession.
QRScannerViewController owns an instance of CameraController and QRScannerView and is their delegate.
getViewControllerToPresent method checks if camera permission is granted by calling checkPermissionsAndLoadCamera of the CameraController.getViewControllerToPresent. If the user has not previously granted camera permission to the application, the QRScannerViewController instance will be returned, and an error will be displayed by the QR scanner if the user denies the permission in the system dialog. The error dialog prompts the user to change this setting and includes a link to the Settings app, if available.QRScannerViewController will be returned as the view controller to present, and the CameraController will start loading the camera on a separate dispatch queue.Camera initialization is handled by the loadCaptureSession method of the CameraController.
CAMERA_UNAVAILABLE and an error dialog is displayed if:AVCaptureSession,AVCaptureSession,CAMERA_AVAILABLE, which is reported asynchronously to QRScannerViewController, and CameraController starts listening for camera notifications.viewWillAppear.hasTorch and isTorchAvailable of the AVCaptureDevice are both YES.AVCaptureTorchModeOn and AVCaptureTorchModeOff.The AVCaptureVideoPreviewLayer is created by the QRScannerView:
QRScannerView is initialized by the QRScannerViewController.viewDidLoad, QRScannerViewController calls loadVideoPreviewLayer: with the loaded preview. If the camera is already loaded, CameraController attaches the preview to the AVCaptureSession. Otherwise the preview is attached immediately after the AVCaptureSession is initialized.The rectangle of interest for the metadata output of the capture session is calculated to lie exactly inside the viewport drawn by the QRScannerView. Resetting the viewport causes the video preview to freeze for a short while, that is why the viewport is only set when the preview is hidden.
QRScannerViewController sets the viewport on viewDidAppear, to make sure that the preview layer is of the correct size and position when the viewport is calculated.CameraController calls the cameraIsReady method of its delegate to notify the QRScannerViewController that the viewport was successfully set and the camera preview can be displayed.CameraController is listening for the following notifications:
AVCaptureSessionRuntimeErrorNotification, handled by setting the camera state to CAMERA_UNAVAILABLE,AVCaptureSessionWasInterruptedNotification, handled by setting the camera state to one of:APPLICATION_IN_BACKGROUND,CAMERA_IN_USE_BY_ANOTHER_APPLICATION,MULTIPLE_FOREGROUND_APPS, based on the value of AVCaptureSessionInterruptionReasonKey in the notification's user info.AVCaptureSessionInterruptionEndedNotification, handled by setting the camera state to CAMERA_AVAILABLE.AVCaptureDeviceWasDisconnected, handled by setting the camera state to CAMERA_UNAVAILABLE.The current state of the torch is obtained using key-value observing of the AVCaptureDevice object.
torchActive property, and the delegate is informed using torchStateChanged:.hasTorch and torchAvailable. Torch is only considered available if both properties are YES and the delegate is informed using torchAvailabilityChanged:.The delegate sets the value of the torch using setTorchMode: and is informed of the outcome asynchronously.
CameraController implements the AVCaptureMetadataOutputObjectsDelegate and receives the scanned result on the main queue.
AVMetadataMachineReadableCodeObject.QRScannerViewController.CameraController checks the type of the scanned code, and if the code can only contain numbers, sets the loadImmediately argument to YES.CameraController stops the capture session.Supported codes (from Machine Readable Object Types):
The QR scanner consists of three views:
VideoPreviewView, PreviewOverlayView and controls as subviews.QRScannerTransitioningDelegate implements a custom transition animation: the QRScannerViewController appears to be positioned below its presenting view controller. The presenting view controller slides up for presentation and down for dismissal.
QRScannerView and PreviewOverlayView rotate normally. VideoPreviewView does not rotate: on viewWillTransitionToSize: the view is animated to rotate in the opposite direction. This avoids resetting the viewport rectangle of interest on every screen rotation, because resetting it causes the video to pause for a while. The view is not positioned using AutoLayout.PreviewOverlayView is a square that is sqrt(2)-times bigger than max(width, height) of the QRScannerView, to avoid redrawing the viewport. This also makes the viewport rotate in place. The view is positioned using AutoLayout.The following metrics are collected:
IOS.Spotlight.Action when the user opens the QR scanner from searching for it in Spotlight.ApplicationShortcut.ScanQRCodePressed when the user opens the QR scanner from 3D Touch application shortcuts.MobileQRScannerClose when the user closes the QR scanner without scanning a code.MobileQRScannerError when the user closes the QR scanner from an error dialog.MobileQRScannerScannedCode when the user scans a code.MobileQRScannerTorchOn when the user switches on the torch.Screen rotation on iPad is not handled the same way as on iPhone, as of iOS 9. The counter-rotation animation is not played at the same time as the screen rotation animation, and the camera preview appears to be rotating. This effect is most visible when rotating the screen by 180 degrees, which results in an apparent double-rotation by 360 degrees.