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.