| Index: ios/chrome/browser/ui/qr_scanner/README.md
|
| diff --git a/ios/chrome/browser/ui/qr_scanner/README.md b/ios/chrome/browser/ui/qr_scanner/README.md
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..91c3f2c4a9b45a2adc7652adf34e1c1d1951f497
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/qr_scanner/README.md
|
| @@ -0,0 +1,280 @@
|
| +# QR Scanner
|
| +
|
| +The QR Scanner provides a way of scanning QR codes and bar codes directly from
|
| +Chrome. It is developed behind the `EnableQRCodeReader` experimental flag.
|
| +
|
| +[TOC]
|
| +
|
| +## Usage
|
| +
|
| +1. Create a delegate implementing the `QRScannerViewControllerDelegate`
|
| + protocol.
|
| +2. Initialize `QRScannerViewController` with this delegate.
|
| +3. Present the view controller returned by `getViewControllerToPresent`.
|
| +
|
| +## Behavior
|
| +
|
| +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.
|
| +
|
| +### Presentation
|
| +
|
| +The QR Scanner is presented using a custom transition animation which makes it
|
| +appear to be originally positioned under the presenting view controller.
|
| +
|
| +### Scanning
|
| +
|
| +* Codes are only recognized inside the viewport.
|
| +* A flash animation is played when a code is recognized.
|
| +* If VoiceOver is enabled, an announcement is played instead of the animation.
|
| +* Scanning a QR code or any other code type which can contain alphanumeric
|
| + strings places the scanned result in the Omnibox, and the user has to press
|
| + the "Go" button on the keyboard to load the result. Non-URL strings will be
|
| + loaded in search.
|
| +* Scanning a bar code type which can only contain numbers will load search
|
| + results containing the bar code immediately without waiting for user
|
| + confirmation.
|
| +
|
| +### Torch
|
| +
|
| +* Torch is switched off every time the QR scanner is opened, closed, or the
|
| + camera session is interrupted.
|
| +* The torch button always reflects the current torch state.
|
| +* The torch button is in a disabled state if the camera does not have torch,
|
| + the torch is unavailable, or the camera is not yet loaded.
|
| +
|
| +### Errors
|
| +
|
| +* A dialog is displayed if the camera is unavailable, camera permissions are
|
| + not granted, the camera is in use by another application, or the application
|
| + is in Split View on iPad.
|
| +* Pressing the "Cancel" button of any error dialog dismisses the QR Scanner
|
| + view controller.
|
| +* If the camera becomes available when a dialog is presented, the dialog is
|
| + automatically dismissed.
|
| +
|
| +## Entry points
|
| +
|
| +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`.
|
| +
|
| +## Controller architecture
|
| +
|
| +* **QRScannerViewController** is the entry point for the feature. It connects
|
| + the `CameraController` and the `QRScannerView` and is responsible for
|
| + displaying alerts from `QRScannerAlerts`.
|
| +* **CameraController** manages the `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][avcapturesession] for
|
| +`AVCaptureSession`.
|
| +
|
| +[avcapturesession]: https://developer.apple.com/reference/avfoundation/avcapturesession
|
| +
|
| +### Initialization
|
| +
|
| +`QRScannerViewController` owns an instance of `CameraController` and
|
| +`QRScannerView` and is their delegate.
|
| +
|
| +1. The `getViewControllerToPresent` method checks if camera permission is
|
| + granted by calling `checkPermissionsAndLoadCamera` of the
|
| + `CameraController`.
|
| +2. If the camera permission is denied, an error dialog will be returned from
|
| + `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.
|
| +3. If the camera permission is granted, the `QRScannerViewController` will be
|
| + returned as the view controller to present, and the `CameraController` will
|
| + start loading the camera on a separate dispatch queue.
|
| +
|
| +#### Camera
|
| +
|
| +Camera initialization is handled by the `loadCaptureSession` method of the
|
| +`CameraController`.
|
| +
|
| +1. Camera state is set to `CAMERA_UNAVAILABLE` and an error dialog is
|
| + displayed if:
|
| + * The back camera of the device is not found,
|
| + * There was an error initializing the camera input,
|
| + * The video input cannot be attached to the `AVCaptureSession`,
|
| + * The metadata output cannot be attached to the `AVCaptureSession`,
|
| + * The metadata output does not support QR code recognition.
|
| +2. After a successful initialization, camera state is set to
|
| + `CAMERA_AVAILABLE`, which is reported asynchronously to
|
| + `QRScannerViewController`, and `CameraController` starts listening for
|
| + camera notifications.
|
| +3. Camera starts recording on `viewWillAppear`.
|
| +
|
| +#### Torch
|
| +
|
| +* Torch availability is checked when the camera initialization completes.
|
| +* Torch is considered available, if the properties `hasTorch` and
|
| + `isTorchAvailable` of the `AVCaptureDevice` are both `YES`.
|
| +* During initialization, it is also checked if the torch supports the torch
|
| + modes `AVCaptureTorchModeOn` and `AVCaptureTorchModeOff`.
|
| +* Torch mode is set to off on initialization.
|
| +
|
| +#### Camera preview
|
| +
|
| +The `AVCaptureVideoPreviewLayer` is created by the `QRScannerView`:
|
| +
|
| +1. The `QRScannerView` is initialized by the `QRScannerViewController`.
|
| +2. On `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.
|
| +
|
| +#### Viewport
|
| +
|
| +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.
|
| +
|
| +1. `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.
|
| +2. If the capture session is loaded, the viewport is set immediately. Otherwise
|
| + the viewport is set after the capture session is loaded.
|
| +3. `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.
|
| +
|
| +### Camera state and notifications
|
| +
|
| +`CameraController` is listening for the following notifications:
|
| +
|
| +1. `AVCaptureSessionRuntimeErrorNotification`, handled by setting the camera
|
| + state to `CAMERA_UNAVAILABLE`,
|
| +2. `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.
|
| +3. `AVCaptureSessionInterruptionEndedNotification`, handled by setting the
|
| + camera state to `CAMERA_AVAILABLE`.
|
| +4. `AVCaptureDeviceWasDisconnected`, handled by setting the camera state to
|
| + `CAMERA_UNAVAILABLE`.
|
| +
|
| +### Torch state
|
| +
|
| +The current state of the torch is obtained using key-value observing of the
|
| +`AVCaptureDevice` object.
|
| +
|
| +* Torch state is set based on the value of the `torchActive` property, and
|
| + the delegate is informed using `torchStateChanged:`.
|
| +* Torch availability is set based on the values of `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.
|
| +
|
| +### Scanning
|
| +
|
| +`CameraController` implements the `AVCaptureMetadataOutputObjectsDelegate` and
|
| +receives the scanned result on the main queue.
|
| +
|
| +* The scanned result must be an `AVMetadataMachineReadableCodeObject`.
|
| +* Only results which are non-empty strings are passed on to the
|
| + `QRScannerViewController`.
|
| +* `CameraController` checks the type of the scanned code, and if the code can
|
| + only contain numbers, sets the `loadImmediately` argument to `YES`.
|
| +* If a valid code was scanned, the `CameraController` stops the capture
|
| + session.
|
| +
|
| +Supported codes (from [Machine Readable Object Types][machinereadableobjects]):
|
| +
|
| +* Numeric-only bar codes: UPC-E, EAN-8, EAN-13, Interleaved 2 of 5, ITF-14
|
| +* Alphanumeric bar codes: Code 39, Code 39 Mod 43, Code 93, Code 128
|
| +* 2D alphanumeric codes: PDF417, AztecCode, DataMatrix, QR Code
|
| +
|
| +[machinereadableobjects]: https://developer.apple.com/reference/avfoundation/avmetadatamachinereadablecodeobject/1668878-machine_readable_object_types?language=objc
|
| +
|
| +## Views
|
| +
|
| +The QR scanner consists of three views:
|
| +
|
| +* **VideoPreviewView** holds the camera preview.
|
| +* **PreviewOverlayView** holds layers drawing the darker preview overlay and
|
| + the viewport border.
|
| +* **QRScannerView** contains the `VideoPreviewView`, `PreviewOverlayView` and
|
| + controls as subviews.
|
| +
|
| +### Transition animation
|
| +
|
| +`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.
|
| +
|
| +### Screen rotation
|
| +
|
| +* `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.
|
| +
|
| +### Split View
|
| +
|
| +* Camera is unavailable in Split View.
|
| +* When the camera becomes available, the viewport rectangle is reset,
|
| + otherwise the viewport would be in the wrong place when Split View is
|
| + cancelled.
|
| +
|
| +## Accessibility
|
| +
|
| +* Standard UI elements have accessibility labels and identifiers.
|
| +* The value of the torch button is communicated using its accessibility value.
|
| +* If VoiceOver is on, an accessibility announcement is played when a code is
|
| + scanned, and the result is loaded after the announcement finishes.
|
| +* If the result is loaded immediately, no additional accessibility notifications are
|
| + posted.
|
| +* If the result is placed in Omnibox for the user to review, the Omnibox
|
| + should be focused afterwards.
|
| +
|
| +## Metrics
|
| +
|
| +The following metrics are collected:
|
| +
|
| +* `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.
|
| +
|
| +## Known issues
|
| +
|
| +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.
|
| +
|
| +## See also
|
| +
|
| +* go/chrome-ios-qr-code for the original design doc.
|
|
|