Commit e3f07bc3 authored by ueshiba's avatar ueshiba
Browse files

WIP

parent 55f41503
......@@ -242,6 +242,7 @@
5EA54E484C7FD035341E021E /* Pods_PIALibrary_PIALibraryTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A22DD34A821A4EDAED288D2 /* Pods_PIALibrary_PIALibraryTests_iOS.framework */; };
843C67C22122E714005A3FDA /* AccountInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843C67C12122E714005A3FDA /* AccountInfoTests.swift */; };
843C67C32122EA13005A3FDA /* AccountInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843C67C12122E714005A3FDA /* AccountInfoTests.swift */; };
84D5DA702126CE2900F753F8 /* QRCameraScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D5DA6F2126CE2900F753F8 /* QRCameraScannerViewController.swift */; };
A56365AB3EAA4588B479616A /* Pods_PIALibrary_PIALibraryHost_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 476E8514A236C48F405ACA40 /* Pods_PIALibrary_PIALibraryHost_iOS.framework */; };
E07462773BC314E3183B240E /* Pods_PIALibrary_PIALibrary_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FC5BF5764146CA9C6104276 /* Pods_PIALibrary_PIALibrary_iOS.framework */; };
/* End PBXBuildFile section */
......@@ -482,6 +483,7 @@
476E8514A236C48F405ACA40 /* Pods_PIALibrary_PIALibraryHost_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIALibrary_PIALibraryHost_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
64C04C071DE1CB44DE7945D6 /* Pods-PIALibrary-PIALibrary-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIALibrary-PIALibrary-iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PIALibrary-PIALibrary-iOS/Pods-PIALibrary-PIALibrary-iOS.debug.xcconfig"; sourceTree = "<group>"; };
843C67C12122E714005A3FDA /* AccountInfoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountInfoTests.swift; sourceTree = "<group>"; };
84D5DA6F2126CE2900F753F8 /* QRCameraScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCameraScannerViewController.swift; sourceTree = "<group>"; };
9A22DD34A821A4EDAED288D2 /* Pods_PIALibrary_PIALibraryTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIALibrary_PIALibraryTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BD0536741E220873045CBCC7 /* Pods-PIALibrary-PIALibraryHost-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIALibrary-PIALibraryHost-iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PIALibrary-PIALibraryHost-iOS/Pods-PIALibrary-PIALibraryHost-iOS.debug.xcconfig"; sourceTree = "<group>"; };
E865E7775CCB07BA4CE22DEE /* Pods_PIALibrary_PIALibrary_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIALibrary_PIALibrary_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
......@@ -769,6 +771,7 @@
0EB8C0541F9CD38A005857E4 /* PurchasePlanCell.swift */,
0EB8C0551F9CD38A005857E4 /* PurchaseViewController.swift */,
0EA8072320A18C6B0033EC1A /* RedeemViewController.swift */,
84D5DA6F2126CE2900F753F8 /* QRCameraScannerViewController.swift */,
0EB8C0561F9CD38A005857E4 /* RestoreSignupViewController.swift */,
0EB8C0571F9CD38A005857E4 /* SignupFailureViewController.swift */,
0EB8C0581F9CD38A005857E4 /* SignupInProgressViewController.swift */,
......@@ -1498,6 +1501,7 @@
0EB9667E1FDF36490086ABC2 /* GlossParser.swift in Sources */,
0EAA38991F9CC7E4000149CF /* AppStoreTransaction.swift in Sources */,
0EA8072720A1A0090033EC1A /* Redeem.swift in Sources */,
84D5DA702126CE2900F753F8 /* QRCameraScannerViewController.swift in Sources */,
0E392D951FE316860002160D /* DebugLog.swift in Sources */,
0EAA38981F9CC7E4000149CF /* AppStoreProduct.swift in Sources */,
0E1743131F82E1A4001E7DD6 /* Client.swift in Sources */,
......
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="VHM-bG-giz">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="VHM-bG-giz">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
......@@ -1139,6 +1139,13 @@ You will not be charged during this process.</string>
<outlet property="delegate" destination="qgN-PX-Sir" id="dW1-A9-fEp"/>
</connections>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aoC-xB-Jhr">
<rect key="frame" x="234" y="117" width="46" height="30"/>
<state key="normal" title="Button"/>
<connections>
<action selector="showCameraToScanQRCodes:" destination="qgN-PX-Sir" eventType="touchUpInside" id="i22-HG-3Xe"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tea-DU-K60" customClass="ActivityButton" customModule="PIALibrary" customModuleProvider="target">
<rect key="frame" x="0.0" y="167" width="280" height="40"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
......@@ -1165,9 +1172,12 @@ You will not be charged during this process.</string>
<constraint firstAttribute="bottom" secondItem="xcj-pL-a9w" secondAttribute="bottom" id="4lI-EV-hFu"/>
<constraint firstItem="pbJ-dg-A3t" firstAttribute="top" secondItem="3ne-g6-NDW" secondAttribute="bottom" constant="20" id="5F7-D0-viR"/>
<constraint firstAttribute="trailing" secondItem="S5y-bL-bNg" secondAttribute="trailing" id="5RT-z4-UFq"/>
<constraint firstAttribute="trailing" secondItem="aoC-xB-Jhr" secondAttribute="trailing" id="925-8A-MSn"/>
<constraint firstItem="tea-DU-K60" firstAttribute="leading" secondItem="TnM-Pl-njO" secondAttribute="leading" id="FYy-Le-HYh"/>
<constraint firstItem="3ne-g6-NDW" firstAttribute="top" secondItem="S5y-bL-bNg" secondAttribute="bottom" constant="20" id="Hgd-B4-frq"/>
<constraint firstItem="pbJ-dg-A3t" firstAttribute="height" secondItem="3ne-g6-NDW" secondAttribute="height" id="IUJ-r9-crT"/>
<constraint firstItem="aoC-xB-Jhr" firstAttribute="top" secondItem="3ne-g6-NDW" secondAttribute="bottom" constant="20" id="JHV-oQ-Ico"/>
<constraint firstItem="aoC-xB-Jhr" firstAttribute="height" secondItem="pbJ-dg-A3t" secondAttribute="height" id="LdG-rX-omr"/>
<constraint firstItem="xcj-pL-a9w" firstAttribute="leading" secondItem="TnM-Pl-njO" secondAttribute="leading" id="Qng-Zc-Xvy"/>
<constraint firstItem="tea-DU-K60" firstAttribute="trailing" secondItem="3ne-g6-NDW" secondAttribute="trailing" id="R8D-e4-h8a"/>
<constraint firstItem="pbJ-dg-A3t" firstAttribute="leading" secondItem="3ne-g6-NDW" secondAttribute="leading" id="Sq9-EB-iDd"/>
......@@ -1176,9 +1186,10 @@ You will not be charged during this process.</string>
<constraint firstAttribute="width" priority="750" constant="280" id="aV8-Hz-Jvp"/>
<constraint firstItem="QEC-Qu-zkj" firstAttribute="top" secondItem="TnM-Pl-njO" secondAttribute="top" id="fcA-aA-3kt"/>
<constraint firstItem="tea-DU-K60" firstAttribute="leading" secondItem="3ne-g6-NDW" secondAttribute="leading" id="jTH-4f-ZLY"/>
<constraint firstItem="tea-DU-K60" firstAttribute="top" secondItem="aoC-xB-Jhr" secondAttribute="bottom" constant="20" id="pbY-F6-mLe"/>
<constraint firstAttribute="trailing" secondItem="pbJ-dg-A3t" secondAttribute="trailing" id="qhK-Wr-Ika"/>
<constraint firstAttribute="trailing" secondItem="xcj-pL-a9w" secondAttribute="trailing" id="qzi-8l-LNR"/>
<constraint firstItem="S5y-bL-bNg" firstAttribute="top" secondItem="QEC-Qu-zkj" secondAttribute="bottom" constant="10" id="scj-YN-W64"/>
<constraint firstItem="pbJ-dg-A3t" firstAttribute="trailing" secondItem="3ne-g6-NDW" secondAttribute="trailing" id="toJ-5G-R2t"/>
<constraint firstItem="S5y-bL-bNg" firstAttribute="leading" secondItem="TnM-Pl-njO" secondAttribute="leading" id="x2Y-Ul-kQx"/>
</constraints>
</view>
......@@ -1338,6 +1349,7 @@ You will not be charged during this process.</string>
</view>
<connections>
<outlet property="buttonRedeem" destination="tea-DU-K60" id="5tX-8w-A1k"/>
<outlet property="cameraButton" destination="aoC-xB-Jhr" id="oNJ-qt-hbR"/>
<outlet property="labelLogin1" destination="Ovb-tk-w2q" id="43z-6a-sg0"/>
<outlet property="labelLogin2" destination="ecj-b6-crK" id="a8l-fb-1Ph"/>
<outlet property="labelSubtitle" destination="S5y-bL-bNg" id="hzd-5h-ycZ"/>
......@@ -1349,17 +1361,33 @@ You will not be charged during this process.</string>
<outlet property="viewFooter" destination="h4C-e9-6n1" id="xcl-1q-9mx"/>
<outlet property="viewLogin" destination="h4C-e9-6n1" id="9QP-x0-OOS"/>
<segue destination="9jt-8I-K8i" kind="presentation" identifier="SignupViaRedeemSegue" id="Knv-fa-JGO"/>
<segue destination="hSc-7t-RTF" kind="presentation" identifier="SignupQRCameraScannerSegue" id="xx6-S9-E8H"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="r3v-al-5qU" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2375" y="-1899"/>
<point key="canvasLocation" x="2373.5999999999999" y="-1899.400299850075"/>
</scene>
<!--Camera Scanner View Controller-->
<scene sceneID="0ML-Wj-AS6">
<objects>
<viewController id="hSc-7t-RTF" customClass="QRCameraScannerViewController" customModule="PIALibrary" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="ep7-uG-1hN">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="bb6-gq-GN1"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="GlS-xa-MJS" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3238" y="-1901"/>
</scene>
</scenes>
<resources>
<image name="nav-logo" width="354" height="36"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="EEe-nI-dHO"/>
<segue reference="Knv-fa-JGO"/>
</inferredMetricsTieBreakers>
</document>
......@@ -38,6 +38,7 @@
"redeem.submit" = "SUBMIT";
"redeem.error.title" = "Redeem";
"redeem.error.code" = "Code must be %lu numeric digits.";
"redeem.error.qrcode.invalid" = "Invalid QR code sequence. Please try again.";
"plan.monthly.title" = "Monthly";
"plan.yearly.title" = "Yearly";
......@@ -57,3 +58,6 @@
"agreement.message" = "Signing up constitutes acceptance of the $1 and the $2.";
"agreement.message.tos" = "Terms of Service";
"agreement.message.privacy" = "Privacy Policy";
"camera.access.error.title" = "Camera not available";
"camera.access.error.message" = "Your device does not support scanning a code. Please use a device with a camera.";
//
// QRCameraScannerViewController.swift
// PIALibrary-iOS
//
// Created by Jose Antonio Blaya Garcia on 17/8/18.
// Copyright © 2018 London Trust Media. All rights reserved.
//
import AVFoundation
import UIKit
class QRCameraScannerViewController: AutolayoutViewController {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
weak var delegate: RedeemScannerDelegate!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if captureSession == nil {
setupCaptureSession()
} else {
if (captureSession?.isRunning == false) {
captureSession.startRunning()
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning()
}
}
override var prefersStatusBarHidden: Bool {
return true
}
private func setupCaptureSession() {
captureSession = AVCaptureSession()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else {
failed()
return
}
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
} catch {
failed()
return
}
if (captureSession.canAddInput(videoInput)) {
captureSession.addInput(videoInput)
} else {
failed()
return
}
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self,
queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
private func found(code: String) {
delegate.giftCardCodeFound(withCode: code)
}
private func failed() {
captureSession = nil
dismissModal()
}
}
extension QRCameraScannerViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput,
didOutput metadataObjects: [AVMetadataObject],
from connection: AVCaptureConnection) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else {
return
}
guard let stringValue = readableObject.stringValue else {
return
}
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
}
dismiss(animated: true)
}
}
......@@ -8,9 +8,14 @@
import UIKit
import SwiftyBeaver
import AVFoundation
private let log = SwiftyBeaver.self
protocol RedeemScannerDelegate: class {
func giftCardCodeFound(withCode code: String)
}
class RedeemViewController: AutolayoutViewController, WelcomeChild {
private static let codeInvalidSet = CharacterSet.decimalDigits.inverted
......@@ -41,6 +46,8 @@ class RedeemViewController: AutolayoutViewController, WelcomeChild {
@IBOutlet private weak var labelLogin1: UILabel!
@IBOutlet private weak var labelLogin2: UILabel!
@IBOutlet private weak var cameraButton: UIButton!
var preset: PIAWelcomeViewController.Preset?
......@@ -144,8 +151,27 @@ class RedeemViewController: AutolayoutViewController, WelcomeChild {
pageController.show(page: .login)
}
@IBAction private func showCameraToScanQRCodes(_ sender: Any?) {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
if response {
DispatchQueue.main.async {
self.perform(segue: StoryboardSegue.Welcome.signupQRCameraScannerSegue)
}
} else {
let alert = Macros.alert(L10n.Welcome.Camera.Access.Error.title,
L10n.Welcome.Camera.Access.Error.message)
alert.addCancelAction(L10n.Ui.Global.close)
self.present(alert, animated: true)
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == StoryboardSegue.Welcome.signupViaRedeemSegue.rawValue) {
switch segue.identifier {
case StoryboardSegue.Welcome.signupViaRedeemSegue.rawValue:
let nav = segue.destination as! UINavigationController
let vc = nav.topViewController as! SignupInProgressViewController
......@@ -162,7 +188,15 @@ class RedeemViewController: AutolayoutViewController, WelcomeChild {
vc.redeemRequest = RedeemRequest(email: email, code: friendlyRedeemCode(code))
vc.preset = preset
vc.completionDelegate = completionDelegate
case StoryboardSegue.Welcome.signupQRCameraScannerSegue.rawValue:
guard let scannerViewController = segue.destination as? QRCameraScannerViewController else {
return
}
scannerViewController.delegate = self
default:
return
}
}
private func enableInteractions(_ enable: Bool) {
......@@ -250,3 +284,18 @@ extension RedeemViewController: UITextViewDelegate {
return true
}
}
extension RedeemViewController: RedeemScannerDelegate {
func giftCardCodeFound(withCode code: String) {
if Validator.validate(giftCode: code) {
textCode.text = friendlyRedeemCode(code)
} else {
let alert = Macros.alert(L10n.Welcome.Redeem.Error.title,
L10n.Welcome.Redeem.Error.Qrcode.invalid)
alert.addCancelAction(L10n.Ui.Global.ok)
self.present(alert, animated: true)
}
}
}
......@@ -84,6 +84,7 @@ enum StoryboardSegue {
case signupViaRecoverSegue = "SignupViaRecoverSegue"
case signupViaRedeemSegue = "SignupViaRedeemSegue"
case signupViaRestoreSegue = "SignupViaRestoreSegue"
case signupQRCameraScannerSegue = "SignupQRCameraScannerSegue"
}
}
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
......
......@@ -167,7 +167,15 @@ enum L10n {
}
/// Redeem
static let title = L10n.tr("Welcome", "redeem.error.title")
enum Qrcode {
/// Invalid QR code sequence. Please try again.
static let invalid = L10n.tr("Welcome", "redeem.error.qrcode.invalid")
}
}
}
enum Restore {
......@@ -183,6 +191,18 @@ enum L10n {
static let placeholder = L10n.tr("Welcome", "restore.email.placeholder")
}
}
enum Camera {
enum Access {
enum Error {
/// Camera not available
static let title = L10n.tr("Welcome", "camera.access.error.title")
/// Your device does not support scanning a code. Please use a device with a camera.
static let message = L10n.tr("Welcome", "camera.access.error.message")
}
}
}
}
enum Ui {
......
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