This file was deleted.

@@ -15,6 +15,7 @@ import RxSwift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

private let disposeBag = DisposeBag()
private let router = AppDelegate.shared?.injection.resolve(AppRouter.self)!

var window: UIWindow?

@@ -32,8 +33,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window?.rootViewController = navigationController
window?.makeKeyAndVisible()

let router = AppDelegate.shared?.injection.resolve(AppRouter.self)!

router?.route(to: Routes.root, from: navigationController)

}
@@ -0,0 +1,130 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//
import Logging
import Foundation

// Adapted from https://nshipster.com/textoutputstream/
struct FileHandlerOutputStream: TextOutputStream {
enum FileHandlerOutputStream: Error {
case couldNotCreateFile
}

private let fileHandle: FileHandle
let encoding: String.Encoding

init(localFile url: URL, encoding: String.Encoding = .utf8) throws {
if !FileManager.default.fileExists(atPath: url.path) {
guard FileManager.default.createFile(atPath: url.path, contents: nil, attributes: nil) else {
throw FileHandlerOutputStream.couldNotCreateFile
}
}

let fileHandle = try FileHandle(forWritingTo: url)
fileHandle.seekToEndOfFile()
self.fileHandle = fileHandle
self.encoding = encoding
}

mutating func write(_ string: String) {
if let data = string.data(using: encoding) {
fileHandle.write(data)
}
}
}

public struct FileLogging {
let stream: TextOutputStream
private var localFile: URL

public init(to localFile: URL) throws {
self.stream = try FileHandlerOutputStream(localFile: localFile)
self.localFile = localFile
}

public func handler(label: String) -> FileLogHandler {
return FileLogHandler(label: label, fileLogger: self)
}

public static func logger(label: String, localFile url: URL) throws -> Logger {
let logging = try FileLogging(to: url)
return Logger(label: label, factory: logging.handler)
}
}

// Adapted from https://github.com/apple/swift-log.git
/// `FileLogHandler` is a simple implementation of `LogHandler` for directing
/// `Logger` output to a local file. Appends log output to this file, even across constructor calls.
public struct FileLogHandler: LogHandler {
private let stream: TextOutputStream
private var label: String

public var logLevel: Logger.Level = .info

private var prettyMetadata: String?
public var metadata = Logger.Metadata() {
didSet {
self.prettyMetadata = self.prettify(self.metadata)
}
}

public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
get {
return self.metadata[metadataKey]
}
set {
self.metadata[metadataKey] = newValue
}
}

public init(label: String, fileLogger: FileLogging) {
self.label = label
self.stream = fileLogger.stream
}

public init(label: String, localFile url: URL) throws {
self.label = label
self.stream = try FileHandlerOutputStream(localFile: url)
}

public func log(level: Logger.Level,
message: Logger.Message,
metadata: Logger.Metadata?,
source: String,
file: String,
function: String,
line: UInt) {
let prettyMetadata = metadata?.isEmpty ?? true
? self.prettyMetadata
: self.prettify(self.metadata.merging(metadata!, uniquingKeysWith: { _, new in new }))

var stream = self.stream
stream.write("\(self.timestamp()) \(level) \(self.label) :\(prettyMetadata.map { " \($0)" } ?? "") \(message)\n")
}

private func prettify(_ metadata: Logger.Metadata) -> String? {
return !metadata.isEmpty ? metadata.map { "\($0)=\($1)" }.joined(separator: " ") : nil
}

private func timestamp() -> String {
var buffer = [Int8](repeating: 0, count: 255)
var timestamp = time(nil)
let localTime = localtime(&timestamp)
strftime(&buffer, buffer.count, "%Y-%m-%dT%H:%M:%S%z", localTime)
return buffer.withUnsafeBufferPointer {
$0.withMemoryRebound(to: CChar.self) {
String(cString: $0.baseAddress!)
}
}
}
}

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "233",
"green" : "233",
"red" : "233"
"blue" : "0.416",
"green" : "0.690",
"red" : "0.345"
}
},
"idiom" : "universal"
@@ -1,20 +1,20 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.969",
"green" : "0.976",
"red" : "0.980",
"alpha" : "1.000"
"red" : "0.980"
}
}
},
"idiom" : "universal"
}
]
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,11 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
@@ -14,7 +9,12 @@
"green" : "0.549",
"red" : "0.506"
}
}
},
"idiom" : "universal"
}
]
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -2,19 +2,19 @@
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"green" : "0.576",
"alpha" : "1.000",
"red" : "0.576",
"blue" : "0.576"
},
"color-space" : "srgb"
"blue" : "0.576",
"green" : "0.576",
"red" : "0.576"
}
},
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -1,20 +1,20 @@
{
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"blue" : "0.929",
"red" : "0.455",
"alpha" : "1.000",
"green" : "0.580"
"blue" : "0.929",
"green" : "0.580",
"red" : "0.455"
}
}
},
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -1,20 +1,20 @@
{
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"red" : "0.878",
"blue" : "0.341",
"green" : "0.424"
"green" : "0.424",
"red" : "0.878"
}
}
},
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -1,20 +1,20 @@
{
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"green" : "0.486",
"blue" : "0.718",
"alpha" : "1.000",
"blue" : "0.718",
"green" : "0.486",
"red" : "0.541"
},
"color-space" : "srgb"
}
}
},
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.655",
"green" : "0.231",
"red" : "0.286"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,7 +1,6 @@
{
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
@@ -10,11 +9,12 @@
"green" : "0.894",
"red" : "0.906"
}
}
},
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -1,20 +1,20 @@
{
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"green" : "0.851",
"blue" : "0.878",
"green" : "0.851",
"red" : "0.827"
},
"color-space" : "srgb"
}
}
},
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -1,20 +1,20 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.612",
"green" : "0.314",
"red" : "0.439",
"alpha" : "1.000"
"red" : "0.439"
}
}
},
"idiom" : "universal"
}
]
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,20 +1,20 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"green" : "0.941",
"blue" : "0.941",
"alpha" : "1.000",
"blue" : "0.941",
"green" : "0.941",
"red" : "0.941"
}
}
},
"idiom" : "universal"
}
]
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.969",
"green" : "0.976",
"red" : "0.980"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,23 +1,23 @@
{
"images" : [
{
"filename" : "Boton 2.png",
"idiom" : "universal",
"filename" : "rectangle.png",
"scale" : "1x"
},
{
"filename" : "Boton 2@2x.png",
"idiom" : "universal",
"scale" : "2x",
"filename" : "rectangle@2x.png"
"scale" : "2x"
},
{
"scale" : "3x",
"filename" : "Boton 2@3x.png",
"idiom" : "universal",
"filename" : "rectangle@3x.png"
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "selected.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "selected@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "selected@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "Frame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "settings_home.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "settings_home@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "settings_home@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,13 +1,13 @@
{
"images" : [
{
"scale" : "1x",
"filename" : "helpCircle.png",
"idiom" : "universal",
"filename" : "helpCircle.png"
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "helpCircle@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
@@ -17,7 +17,7 @@
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
File renamed without changes.
Diff not rendered.
Diff not rendered.
@@ -2,13 +2,13 @@
"images" : [
{
"filename" : "helpCircle.png",
"scale" : "1x",
"idiom" : "universal"
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "helpCircle@2x.png",
"scale" : "2x",
"idiom" : "universal"
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "helpCircle@3x.png",
@@ -17,7 +17,7 @@
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
Diff not rendered.
Diff not rendered.
Diff not rendered.
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "Group 82.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group 82@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group 82@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Diff not rendered.
Diff not rendered.
Diff not rendered.
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "Group 82.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group 82@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group 82@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Diff not rendered.
Diff not rendered.
Diff not rendered.
@@ -1,8 +1,8 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "iconsMenuInfoNormal.png",
"idiom" : "universal",
"scale" : "1x"
},
{
@@ -11,13 +11,13 @@
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "iconsMenuInfoNormal@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
Diff not rendered.
Diff not rendered.
Diff not rendered.
@@ -6,18 +6,18 @@
"scale" : "1x"
},
{
"scale" : "2x",
"filename" : "iconsMenuInfoNormal@2x.png",
"idiom" : "universal",
"filename" : "iconsMenuInfoNormal@2x.png"
"scale" : "2x"
},
{
"filename" : "iconsMenuInfoNormal@3x.png",
"idiom" : "universal",
"scale" : "3x",
"filename" : "iconsMenuInfoNormal@3x.png"
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
Diff not rendered.
Diff not rendered.
Diff not rendered.
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "settings.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "settings@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "settings@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Diff not rendered.
Diff not rendered.
Diff not rendered.
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "settings.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "settings@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "settings@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Diff not rendered.
Diff not rendered.
Diff not rendered.
File renamed without changes.
Diff not rendered.
Diff not rendered.
Diff not rendered.

This file was deleted.

@@ -0,0 +1,36 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//
import UIKit

class BaseViewController: UIViewController {

@IBOutlet weak var backButton: UIButton?

override func viewDidLoad() {
super.viewDidLoad()

setAccesibilityBackButton()
}

private func setAccesibilityBackButton() {

//Set Accesibility logic
backButton?.isAccessibilityElement = true
let previous = navigationController?.previousViewController
if let strTitle = (previous as? AccTitleView)?.accTitle ?? previous?.title {
backButton?.accessibilityLabel = "\(strTitle)," + "ACC_BUTTON_BACK_TO".localized
} else {
backButton?.accessibilityLabel = "ACC_BUTTON_BACK".localized
}
backButton?.accessibilityHint = "ACC_HINT".localized
}
}
@@ -0,0 +1,33 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//
import UIKit

class CustomSlider: UISlider {

//To set line height value from IB, here ten is default value
@IBInspectable var trackLineHeight: CGFloat = 18

//To set custom size of track so here override trackRect function of slider control
override func trackRect(forBounds bound: CGRect) -> CGRect {
//Here, set track frame
return CGRect(origin: bound.origin, size: CGSize(width: bound.width, height: trackLineHeight))
}

override func draw(_ rect: CGRect) {

if self.value == self.maximumValue {
self.value = (self.value - 0.1)
self.maximumTrackTintColor = UIColor.degradado
self.minimumTrackTintColor = UIColor.degradado
}
}
}
@@ -0,0 +1,46 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//
import UIKit

class CustomSliderView: UIView, XibInstantiatable {

@IBOutlet weak var slider: CustomSlider!
@IBOutlet weak var stepLabel: UILabel!
@IBOutlet weak var viewContainer: UIView!

override init(frame: CGRect) {
super.init(frame: frame)
instantiate()
self.translatesAutoresizingMaskIntoConstraints = false
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
instantiate()
}

func configure(indexStep: Int, totalStep: Int, accesibilityHelper: String? = nil) {

stepLabel.text = "MY_HEALTH_RANGER".localized
stepLabel.text = stepLabel.text?.replacingOccurrences(of: "$1", with: "\(indexStep)")
stepLabel.text = stepLabel.text?.replacingOccurrences(of: "$2", with: "\(totalStep)")

if let strAccesibility = accesibilityHelper {
viewContainer.accessibilityLabel = strAccesibility
}

//Config slider
slider.maximumValue = 1
slider.maximumValue = Float(totalStep)
slider.value = Float(indexStep)
}
}
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CustomSliderView" customModule="Radar_COVID" customModuleProvider="target">
<connections>
<outlet property="slider" destination="cLw-D7-Kjk" id="gfM-eP-8LX"/>
<outlet property="stepLabel" destination="o6R-S9-XpD" id="RjR-iM-bOP"/>
<outlet property="viewContainer" destination="k7M-g6-tVc" id="0fq-IJ-GkA"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="258" height="54"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="k7M-g6-tVc">
<rect key="frame" x="0.0" y="0.0" width="258" height="54"/>
<subviews>
<slider opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="2" continuous="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cLw-D7-Kjk" customClass="CustomSlider" customModule="Radar_COVID" customModuleProvider="target">
<rect key="frame" x="-2" y="0.0" width="262" height="55"/>
<color key="tintColor" name="degradado"/>
<accessibility key="accessibilityConfiguration">
<bool key="isElement" value="NO"/>
</accessibility>
<color key="thumbTintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="trackLineHeight">
<real key="value" value="18"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="o6R-S9-XpD">
<rect key="frame" x="0.0" y="54" width="258" height="0.0"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<accessibility key="accessibilityConfiguration">
<bool key="isElement" value="NO"/>
</accessibility>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<accessibility key="accessibilityConfiguration">
<bool key="isElement" value="YES"/>
</accessibility>
<constraints>
<constraint firstAttribute="trailing" secondItem="o6R-S9-XpD" secondAttribute="trailing" id="12C-gf-tqx"/>
<constraint firstItem="o6R-S9-XpD" firstAttribute="top" secondItem="cLw-D7-Kjk" secondAttribute="bottom" id="1uv-Zn-gsf"/>
<constraint firstItem="cLw-D7-Kjk" firstAttribute="leading" secondItem="k7M-g6-tVc" secondAttribute="leading" id="INv-DW-0Rf"/>
<constraint firstAttribute="trailing" secondItem="cLw-D7-Kjk" secondAttribute="trailing" id="RXe-U5-WPE"/>
<constraint firstAttribute="bottom" secondItem="o6R-S9-XpD" secondAttribute="bottom" id="T5a-F1-0JX"/>
<constraint firstItem="o6R-S9-XpD" firstAttribute="leading" secondItem="k7M-g6-tVc" secondAttribute="leading" id="haI-Zn-PBM"/>
<constraint firstItem="cLw-D7-Kjk" firstAttribute="top" secondItem="k7M-g6-tVc" secondAttribute="top" id="uzk-kQ-u6Y"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="k7M-g6-tVc" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="5GO-DQ-9kA"/>
<constraint firstAttribute="bottom" secondItem="k7M-g6-tVc" secondAttribute="bottom" id="VKj-I1-HQv"/>
<constraint firstItem="k7M-g6-tVc" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="gLp-8q-5nI"/>
<constraint firstAttribute="trailing" secondItem="k7M-g6-tVc" secondAttribute="trailing" id="moa-Mr-I6S"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-423" y="-74"/>
</view>
</objects>
<resources>
<namedColor name="degradado">
<color red="0.28627450980392155" green="0.23137254901960785" blue="0.65490196078431373" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>
@@ -0,0 +1,94 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//
import Foundation
import UIKit
protocol SwitchStateListener {
func stateChanged(active: Bool, switcher: CustomSwitch)
}
class CustomSwitch: UIView, XibInstantiatable {

@IBOutlet weak var stateImage: UIImageView!
@IBOutlet weak var regularSwitch: UISwitch!
@IBOutlet weak var container: UIView!

@IBInspectable var swicherType: String?

var isTermSwitcher: Bool {
return swicherType == "terms"
}
var isPolicySwitcher: Bool {
return swicherType == "policy"
}

var delegate: SwitchStateListener?
var active = false
var switchStateImage: UIImage? {
return self.active ?
UIImage(named: "CheckboxSelected")
: UIImage(named: "CheckboxUnselected")
}

override init(frame: CGRect) {
super.init(frame: frame)
instantiate()
self.setupAccessibility()
self.translatesAutoresizingMaskIntoConstraints = false
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
instantiate()
self.setupAccessibility()
}


func updateViews() {
if !UIAccessibility.isVoiceOverRunning {
self.stateImage.image = self.switchStateImage
}
}

func setupAccessibility() {
container.isUserInteractionEnabled = true

if UIAccessibility.isVoiceOverRunning {
self.stateImage.isHidden = true
self.regularSwitch.isHidden = false
} else {
container.addGestureRecognizer(
UITapGestureRecognizer(target: self, action: #selector(toggleState))
)
self.stateImage.isHidden = false
self.regularSwitch.isHidden = true

}

regularSwitch.accessibilityTraits = UISwitch().accessibilityTraits
regularSwitch.accessibilityHint = "ACC_HINT".localized
}

func setAccesibilityLabel(accessibilityLabel: String) {
regularSwitch.accessibilityLabel = accessibilityLabel
}

@objc func toggleState(){
self.active = !active
delegate?.stateChanged(active: self.active, switcher: self)
updateViews()
}


@IBAction func onSwithChange(_ sender: Any) {
toggleState()
}

}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CustomSwitch" customModule="Radar_COVID" customModuleProvider="target">
<connections>
<outlet property="container" destination="sdI-Gq-5J5" id="hyD-eY-cza"/>
<outlet property="regularSwitch" destination="35V-uJ-mhN" id="6zv-UX-1oG"/>
<outlet property="stateImage" destination="nkg-yX-hqd" id="25D-8a-4Zt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="49" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sdI-Gq-5J5">
<rect key="frame" x="0.0" y="0.0" width="49" height="40"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="CheckboxUnselected" translatesAutoresizingMaskIntoConstraints="NO" id="nkg-yX-hqd">
<rect key="frame" x="15.5" y="3" width="18" height="18"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" none="YES"/>
<bool key="isElement" value="NO"/>
</accessibility>
<constraints>
<constraint firstAttribute="width" constant="18" id="C3v-iO-uEM"/>
<constraint firstAttribute="height" constant="18" id="t8z-u5-HS3"/>
</constraints>
</imageView>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="35V-uJ-mhN">
<rect key="frame" x="0.0" y="0.0" width="51" height="31"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" none="YES"/>
</accessibility>
<constraints>
<constraint firstAttribute="width" constant="49" id="dWk-OB-Z7g"/>
</constraints>
<connections>
<action selector="onSwithChange:" destination="-1" eventType="valueChanged" id="4Zb-6e-Adn"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstItem="35V-uJ-mhN" firstAttribute="top" secondItem="sdI-Gq-5J5" secondAttribute="top" id="3ug-OJ-vJl"/>
<constraint firstItem="35V-uJ-mhN" firstAttribute="leading" secondItem="sdI-Gq-5J5" secondAttribute="leading" id="7RH-db-FfA"/>
<constraint firstAttribute="height" constant="40" id="Lwg-TB-dcq"/>
<constraint firstItem="nkg-yX-hqd" firstAttribute="top" secondItem="sdI-Gq-5J5" secondAttribute="top" constant="3" id="Szv-Fb-ter"/>
<constraint firstAttribute="trailing" secondItem="35V-uJ-mhN" secondAttribute="trailing" id="lFg-Qg-zcU"/>
<constraint firstItem="nkg-yX-hqd" firstAttribute="centerX" secondItem="sdI-Gq-5J5" secondAttribute="centerX" id="v82-cp-H6q"/>
<constraint firstItem="35V-uJ-mhN" firstAttribute="centerX" secondItem="sdI-Gq-5J5" secondAttribute="centerX" id="zUq-dV-D4G"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="sdI-Gq-5J5" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="oYT-8M-NjE"/>
<constraint firstItem="sdI-Gq-5J5" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="zXf-iH-sQ0"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-371.73913043478262" y="-220.98214285714286"/>
</view>
</objects>
<resources>
<image name="CheckboxUnselected" width="16" height="16"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
@@ -0,0 +1,29 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//
import UIKit

protocol XibInstantiatable {
func instantiate()
}

extension XibInstantiatable where Self: UIView {
func instantiate() {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else {
return
}
view.frame = self.bounds
insertSubview(view, at: 0)

view.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight]
}
}
@@ -19,6 +19,8 @@ class LocalizationHolder {
get {
if let source = source {
_localizationMap = source.localizationMap
}else{
_localizationMap = UserDefaultsLocalizationRepository().getTexts()
}
return _localizationMap
}
@@ -59,11 +59,15 @@ class NotificationHandler: NSObject, UNUserNotificationCenterDelegate {
var title, body: String?
var sound: UNNotificationSound?
formatter.dateFormat = "dd.MM.YYYY"

switch expositionInfo.level {
case .exposed:
title = "NOTIFICATION_TITLE_EXPOSURE_HIGH".localized
body = "NOTIFICATION_MESSAGE_EXPOSURE_HIGH".localized

title = title == "NOTIFICATION_TITLE_EXPOSURE_HIGH" ? "Riesgo de exposición alto." : title
body = body == "NOTIFICATION_TITLE_EXPOSURE_HIGH" ? "Tu exposición ahora es alta" : body

sound = .defaultCritical
default:
debugPrint("No notification for exposition: \(expositionInfo.level.rawValue)")
@@ -9,39 +9,68 @@
// SPDX-License-Identifier: MPL-2.0
//

import Foundation

enum ExponentialDistribution {
/// Get a random double using an exponential distribution
/// - Parameter rate: The inverse of the upper limit
/// - Returns: A random double between 0 and the limit
static func sample(rate: Double = 1.0) -> Double {
assert(rate > 0, "Cannot divide by 0")
// We use -log(1-U) since U is [0, 1)
return -log(1 - Double.random(in: 0 ..< 1)) / rate
}
}

class FakeRequestRepository {

private var userDefaults: UserDefaults
private let lastFake = "UserDefaultsFakeRequestUseCase.lastFake"

private var dateThreeHoursAgo:Date {
let currentDate = Date()
var dateComponent = DateComponents()
dateComponent.hour = 3
dateComponent.minute = -30
return Calendar.current.date(
byAdding: dateComponent,
to: currentDate)
?? Date()
private let nextFakeRequestDate = "UserDefaultsFakeRequestUseCase.lastFake"
var rate: Double
let daySecs: Double = 24 * 60 * 60

private var now: Date {
Date()
}

private var _fakeRequestDate: Date?
var fakeRequestDate: Date {
private var _nextScheduledFakeRequestDate: Date {
get {
self._fakeRequestDate ??
userDefaults.value(forKey: lastFake) as? Date
?? self.dateThreeHoursAgo
userDefaults.value(forKey: nextFakeRequestDate) as? Date
?? setNextScheduledFakeRequestDate()
}
set(newDate) {
self._fakeRequestDate = newDate
userDefaults.set(newDate, forKey: lastFake)
userDefaults.synchronize()
set(newVal) {
userDefaults.setValue(newVal, forKey: self.nextFakeRequestDate)
}
}

init() {
rate = 1.0
if Config.debug {
rate = 1
}
self.userDefaults = UserDefaults.standard
rate = 1.0
if Config.debug {
rate = 0.1
}
}

public func getNextScheduledFakeRequestDate() -> Date {
return self._nextScheduledFakeRequestDate
}

private func setNextScheduledFakeRequestDate() -> Date {
let nextFakeDate = Date(timeInterval: ExponentialDistribution.sample(rate: rate) * daySecs, since: now)
self._nextScheduledFakeRequestDate = nextFakeDate
print("setting new scheduled fake request date", nextFakeDate, "actualDate", Date())
return nextFakeDate
}

public func updateScheduledFakeRequestDate() {
let _ = self.setNextScheduledFakeRequestDate()
}

}


@@ -12,69 +12,78 @@
import Foundation

protocol LocalizationRepository {

func getLocale() -> String?
func setLocale(_ locale: String)

func getLocales() -> [String: String?]?
func setLocales(_ locales: [String: String?])

func getLocales() -> [ItemLocale]?
func setLocales(_ locales: [ItemLocale])
func setTexts(_ texts: [String: String])
func getTexts() -> [String: String]?

func setCurrent(ca: CaData)
func getCurrentCA() -> CaData?
}

class UserDefaultsLocalizationRepository: LocalizationRepository {

private static let kLocale = "UserDefaultsLocalizationRepository.locale"
private static let kLocales = "UserDefaultsLocalizationRepository.kLocales"
private static let kTexts = "UserDefaultsLocalizationRepository.texts"
private static let kCCAA = "UserDefaultsLocalizationRepository.kCCAA"
private static let kCurrentCA = "UserDefaultsLocalizationRepository.kCurrentCa"

private let userDefaults: UserDefaults

init() {
userDefaults = UserDefaults(suiteName: Bundle.main.bundleIdentifier) ?? UserDefaults.standard
}

func getLocale() -> String? {
userDefaults.object(forKey: UserDefaultsLocalizationRepository.kLocale) as? String ?? self.getLocales()?
.keys.filter({ (key) -> Bool in
key.contains("es")
}).first
.filter({ (itemLocale) -> Bool in
itemLocale.id.contains("es")
}).first?.id
}

func setLocale(_ locale: String) {
userDefaults.set(locale, forKey: UserDefaultsLocalizationRepository.kLocale)
userDefaults.set(locale, forKey: UserDefaultsLocalizationRepository.kLocale)
}

func setTexts(_ texts: [String: String]) {
userDefaults.set(texts, forKey: UserDefaultsLocalizationRepository.kTexts)
}

func getTexts() -> [String: String]? {
userDefaults.object(forKey: UserDefaultsLocalizationRepository.kTexts) as? [String: String]
}

func getCurrentCA() -> CaData? {
try? PropertyListDecoder().decode(CaData.self,
from: userDefaults.object(forKey: UserDefaultsLocalizationRepository.kCurrentCA) as? Data ?? Data())
from: userDefaults.object(forKey: UserDefaultsLocalizationRepository.kCurrentCA) as? Data ?? Data())
}

func setCurrent(ca: CaData) {
let data = try? PropertyListEncoder().encode(ca)
userDefaults.set(data, forKey: UserDefaultsLocalizationRepository.kCurrentCA)
}

func getLocales() -> [String: String?]? {
userDefaults.object(forKey: UserDefaultsLocalizationRepository.kLocales) as? [String: String?]

func getLocales() -> [ItemLocale]? {
if let dLocales = userDefaults.object(forKey: UserDefaultsLocalizationRepository.kLocales) as? [String: String?] {
return ItemLocale.mappertToDic(dic: dLocales)
} else if let dataLocales = userDefaults.object(forKey: UserDefaultsLocalizationRepository.kLocales) as? Data {
let locales = try! JSONDecoder().decode(Locales.self, from: dataLocales)
return locales.itemLocales
}
return nil
}

func setLocales(_ localea: [String: String?]) {
userDefaults.set(localea, forKey: UserDefaultsLocalizationRepository.kLocales)

func setLocales(_ locale: [ItemLocale]) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(Locales.init(itemLocales: locale)) {
userDefaults.set(encoded, forKey: UserDefaultsLocalizationRepository.kLocales)
}
}

}
@@ -0,0 +1,34 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//

import Foundation
class TermsAcceptedRepository {

private var userDefaults: UserDefaults
private let termsAcceptedKey = "UserDefaultsTermsAccepted.accepted"

init() {
self.userDefaults = UserDefaults.standard
}

var termsAccepted: Bool {
get {
return userDefaults.bool(forKey: termsAcceptedKey)
}
set(newVal) {
userDefaults.setValue(newVal, forKey: termsAcceptedKey)
}
}

}


@@ -27,8 +27,9 @@ open class VerificationControllerAPI {
- parameter body: (body)
- parameter completion: completion handler to receive the data and the error objects
*/
open func verifyCode(body: Code, completion: @escaping ((_ data: TokenResponse?, _ error: Error?) -> Void)) {
verifyCodeWithRequestBuilder(body: body).execute { (response, error) -> Void in
open func verifyCode(body: Code, share: Bool, completion: @escaping ((_ data: TokenResponse?, _ error: Error?) -> Void)) {
let headers = ["X-EFGS-Sharing": share ? "1" : "0"]
verifyCodeWithRequestBuilder(body: body, headers: headers).execute { (response, error) -> Void in
completion(response?.body, error)
}
}
@@ -38,9 +39,9 @@ open class VerificationControllerAPI {
- parameter body: (body)
- returns: Observable<TokenResponse>
*/
open func verifyCode(body: Code) -> Observable<TokenResponse> {
open func verifyCode(body: Code, share: Bool) -> Observable<TokenResponse> {
return Observable.create { [weak self] observer -> Disposable in
self?.verifyCode(body: body) { data, error in
self?.verifyCode(body: body, share: share) { data, error in
if let error = error {
observer.on(.error(error))
} else {
@@ -63,16 +64,16 @@ open class VerificationControllerAPI {
- returns: RequestBuilder<TokenResponse>
*/
open func verifyCodeWithRequestBuilder(body: Code) -> RequestBuilder<TokenResponse> {
open func verifyCodeWithRequestBuilder(body: Code, headers: [String: String] = [:] ) -> RequestBuilder<TokenResponse> {
let path = "/verify/code"
let URLString = clientApi.basePath + path
let parameters = JSONEncodingHelper.encodingParameters(forEncodableObject: body)

let url = URLComponents(string: URLString)

let requestBuilder: RequestBuilder<TokenResponse>.Type = SwaggerClientAPI.requestBuilderFactory.getBuilder()

return requestBuilder.init(method: "POST", URLString: (url?.string ?? URLString), parameters: parameters, isBody: true)
return requestBuilder.init(method: "POST", URLString: (url?.string ?? URLString), parameters: parameters, isBody: true, headers: headers)
}

}
@@ -31,9 +31,9 @@ class DiagnosisCodeUseCase {
dateFormatter.locale = Locale(identifier: "es_ES")
}

func sendDiagnosisCode(code: String, date: Date? = nil) -> Observable<Bool> {
func sendDiagnosisCode(code: String, date: Date? = nil, share: Bool = false) -> Observable<Bool> {
self.isfake = FakeRequestUseCase.FALSE_POSITIVE_CODE == code
return verificationApi.verifyCode(body: Code( date: date, code: code ) )
return verificationApi.verifyCode(body: Code( date: date, code: code ), share: share )
.catchError { [weak self] error in throw self?.mapError(error) ?? error }
.flatMap { [weak self] tokenResponse -> Observable<Bool> in
guard let jwtOnset = try self?.parseToken(tokenResponse.token).claims.onset,
@@ -42,7 +42,6 @@ class DiagnosisCodeUseCase {
}
return self?.iWasExposed(onset: onset, token: tokenResponse.token) ?? .empty()
}

}

private func iWasExposed(onset: Date, token: String) -> Observable<Bool> {
@@ -30,7 +30,6 @@ class ExpositionCheckUseCase {
func checkBackToHealthyJustChanged() -> Bool {
let changed = expositionInfoRepository.isChangedToHealthy() ?? false
expositionInfoRepository.setChangedToHealthy(changed: false)
self.resetDataUseCase.resetExposureDays().subscribe().disposed(by: disposeBag)
return changed
}

@@ -13,24 +13,24 @@ import DP3TSDK
import Foundation
import ExposureNotification
import RxSwift
import Logging

class ExpositionUseCase: DP3TTracingDelegate {

private let logger = Logger(label: "ExpositionUseCase")

private let disposeBag = DisposeBag()
private let dateFormatter = DateFormatter()

private let subject: BehaviorSubject<ExpositionInfo>
private let expositionInfoRepository: ExpositionInfoRepository
private let localizationUseCase: LocalizationUseCase
private let notificationHandler: NotificationHandler

init(notificationHandler: NotificationHandler,
expositionInfoRepository: ExpositionInfoRepository,
localizationUseCase: LocalizationUseCase) {
expositionInfoRepository: ExpositionInfoRepository) {

self.notificationHandler = notificationHandler
self.expositionInfoRepository = expositionInfoRepository
self.localizationUseCase = localizationUseCase

self.subject = BehaviorSubject<ExpositionInfo>(
value: expositionInfoRepository.getExpositionInfo() ?? ExpositionInfo(level: .healthy)
@@ -43,45 +43,36 @@ class ExpositionUseCase: DP3TTracingDelegate {

func DP3TTracingStateChanged(_ state: TracingState) {

if var expositionInfo = tracingStatusToExpositionInfo(tStatus: state) {

let localEI = expositionInfoRepository.getExpositionInfo()
log(tracingState: state)
var expositionInfo = tracingStatusToExpositionInfo(tStatus: state)

if isNewInfected(localEI, expositionInfo) {
expositionInfo.since = Date()
}
if showNotification(localEI, expositionInfo) {

//Check if have localization sending notifications with key text
localizationUseCase.loadlocalization().subscribe(
onNext: { [weak self] _ in
self?.notificationHandler.scheduleNotification(expositionInfo: expositionInfo)
}).disposed(by: self.disposeBag)
}
if expositionInfo.error == nil {
expositionInfoRepository.save(expositionInfo: expositionInfo)
}
let localEI = expositionInfoRepository.getExpositionInfo()

subject.onNext(expositionInfo)
if isNewInfected(localEI, expositionInfo) {
expositionInfo.since = Date()
}
if showNotification(localEI, expositionInfo) {
notificationHandler.scheduleNotification(expositionInfo: expositionInfo)
}
if expositionInfo.isOk() {
expositionInfoRepository.save(expositionInfo: expositionInfo)
}

subject.onNext(expositionInfo)

}

func getExpositionInfo() -> Observable<ExpositionInfo> {
subject.asObservable()
}

func getExpositionInfoFromRepository() -> ExpositionInfo! {
return expositionInfoRepository.getExpositionInfo() ?? ExpositionInfo(level: .healthy)
}

func updateExpositionInfo() {

DP3TTracing.status { result in
switch result {
case let .success(state):
if let eis = tracingStatusToExpositionInfo(tStatus: state) {
subject.onNext(eis)
}
subject.onNext(tracingStatusToExpositionInfo(tStatus: state))
case let .failure(error):
subject.onError(error)
}
@@ -90,7 +81,7 @@ class ExpositionUseCase: DP3TTracingDelegate {
}

// Metodo para mapear un TracingState a un ExpositionInfo
private func tracingStatusToExpositionInfo(tStatus: TracingState) -> ExpositionInfo? {
private func tracingStatusToExpositionInfo(tStatus: TracingState) -> ExpositionInfo {

switch tStatus.trackingState {
case .inactive(let error):
@@ -150,5 +141,16 @@ class ExpositionUseCase: DP3TTracingDelegate {
}

}

private func log(tracingState: TracingState) {
logger.debug("New Tracing State --->")
logger.debug("Status: \(tracingState.infectionStatus)")
logger.debug("lastSync: \(String(describing: tracingState.lastSync))")
logger.debug("tracingState \(tracingState.trackingState)")
if case let .inactive(error) = tracingState.trackingState {
logger.debug("Error: \(error)")
}
logger.debug("<---")
}

}
@@ -26,10 +26,11 @@ class FakeRequestUseCase: DiagnosisCodeUseCase {
func sendFalsePositive() -> Observable<Bool> {
return Observable.create { [weak self] (observer) -> Disposable in
if self?.needToSendFalsePositive() ?? false {
self?.sendDiagnosisCode(code: FakeRequestUseCase.FALSE_POSITIVE_CODE).subscribe(
let randomBoolean = Bool.random()
self?.sendDiagnosisCode(code: FakeRequestUseCase.FALSE_POSITIVE_CODE, date: Date(), share: randomBoolean).subscribe(
onNext: { _ in

self?.fakeRequestRepository.fakeRequestDate = Date()
print("fake request sended with date", Date())
self?.fakeRequestRepository.updateScheduledFakeRequestDate()
return observer.onNext(true)

}
@@ -46,7 +47,8 @@ class FakeRequestUseCase: DiagnosisCodeUseCase {
}

private func needToSendFalsePositive() -> Bool{
return abs(fakeRequestRepository.fakeRequestDate.timeIntervalSinceNow) >= minFakeRequestTimeSpan
let interval = self.fakeRequestRepository.getNextScheduledFakeRequestDate().timeIntervalSince(Date())
return interval <= 2 * 24 * 60 * 60
}

}
@@ -17,22 +17,21 @@ class LocalesUseCase {
private let localizationRepository: LocalizationRepository
private let masterDataApi: MasterDataAPI

private var locales: [String: String?]?
private var locales: [ItemLocale]?

init(localizationRepository: LocalizationRepository,
masterDataApi: MasterDataAPI) {
self.localizationRepository = localizationRepository
self.masterDataApi = masterDataApi
}

public func loadLocales() -> Observable<[String: String?]> {
public func loadLocales() -> Observable<[ItemLocale]> {
let currentLocale = localizationRepository.getLocale()
return masterDataApi.getLocales(locale: currentLocale, platform: Config.platform, version: Config.version).map { [weak self] masterLocales in
var locales: [String: String?] = [:]
var locales: [ItemLocale] = []

masterLocales.forEach { loc in
if let localId = loc.id {
locales[localId] = loc.description
}
locales.append(ItemLocale.mappertToKeyValueDto(keyValueDto: loc))
}
print(locales)
self?.locales = locales
@@ -41,7 +40,7 @@ class LocalesUseCase {
}
}

public func getLocales() -> Observable<[String: String?]> {
public func getLocales() -> Observable<[ItemLocale]> {
.deferred { [weak self] in
if let locales = self?.locales {
return .just(locales)
@@ -32,5 +32,9 @@ struct CaData: Codable {
self.web = web
self.webName = webName
}

static func emptyCaData() -> CaData {
return CaData.init(id: "", description: "", phone: "", email: "", web: "", webName: "", additionalInfo: "")
}

}
@@ -27,5 +27,9 @@ struct ExpositionInfo: Codable, Equatable {
case exposed
case infected
}

func isOk() -> Bool {
error == nil
}

}
@@ -0,0 +1,36 @@
//
// Copyright (c) 2020 Gobierno de España
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
//
import Foundation

struct ItemLocale: Codable {

let id: String
let description: String

static func mappertToKeyValueDto(keyValueDto: KeyValueDto) -> ItemLocale {
ItemLocale.init(id: keyValueDto.id ?? "", description: keyValueDto.description ?? "")
}

static func mappertToDic(dic: [String: String?]) -> [ItemLocale] {
var locales: [ItemLocale] = []

dic.forEach { loc in
locales.append(ItemLocale.init(id: loc.key, description: loc.value ?? ""))
}
return locales
}
}

struct Locales: Codable {

let itemLocales: [ItemLocale]
}
@@ -13,12 +13,15 @@ import Foundation
import ExposureNotification
import DP3TSDK
import RxSwift
import Logging

class SetupUseCase: LoggingDelegate, ActivityDelegate, DP3TBackgroundHandler {

private let dateFormatter = DateFormatter()

private let disposeBag = DisposeBag()

private let logger = Logger(label: "DP3TSDK")

private let preferencesRepository: PreferencesRepository
private let notificationHandler: NotificationHandler
@@ -42,6 +45,8 @@ class SetupUseCase: LoggingDelegate, ActivityDelegate, DP3TBackgroundHandler {
func initializeSDK() throws {

let url = URL(string: Config.endpoints.dpppt)!

DP3TTracing.loggingEnabled = true

DP3TTracing.loggingDelegate = self
DP3TTracing.activityDelegate = self
@@ -55,45 +60,46 @@ class SetupUseCase: LoggingDelegate, ActivityDelegate, DP3TBackgroundHandler {
}

func log(_ string: String, type: OSLogType) {
// debugPrint(string)
logger.debug("\(string)")
}

func syncCompleted(totalRequest: Int, errors: [DP3TTracingError]) {
debugPrint("DP3T Sync totalRequest \(totalRequest)")
logger.debug("DP3T Sync totalRequest \(totalRequest)")
for error in errors {
debugPrint("DP3T Sync error \(error)")
logger.error("DP3T Sync error \(error)")
}
preferencesRepository.setLastSync(date: Date())

expositionCheckUseCase.checkBackToHealthy().subscribe(onError: { error in
debugPrint("Error up checking exposed to healthy state \(error)")
}, onCompleted: {
debugPrint("Expostion Check completed")
expositionCheckUseCase.checkBackToHealthy().subscribe(onError: { [weak self] error in
self?.logger.error("Error up checking exposed to healthy state \(error)")
}, onCompleted: { [weak self] in
self?.logger.error("Expostion Check completed")
}).disposed(by: disposeBag)
}

func fakeRequestCompleted(result: Result<Int, DP3TNetworkingError>) {
debugPrint("DP3T Fake request completed...")
logger.debug("DP3T Fake request completed...")
}

func outstandingKeyUploadCompleted(result: Result<Int, DP3TNetworkingError>) {
debugPrint("DP3T OutstandingKeyUpload...")
logger.debug("DP3T OutstandingKeyUpload...")
}

func exposureSummaryLoaded(summary: ENExposureDetectionSummary) {
traceSummary(summary)
}

private func traceSummary(_ summary: ENExposureDetectionSummary ) {
debugPrint("ENExposureDetectionSummary received")
debugPrint("- daysSinceLastExposure: \(summary.daysSinceLastExposure)")
debugPrint("- matchedKeyCount: \(summary.matchedKeyCount)")
debugPrint("- maximumRiskScore: \(summary.maximumRiskScore)")
debugPrint("- riskScoreSumFullRange: \(String(describing: summary.metadata?["riskScoreSumFullRange"]))")
logger.debug("ENExposureDetectionSummary received")
logger.debug("- daysSinceLastExposure: \(summary.daysSinceLastExposure)")
logger.debug("- matchedKeyCount: \(summary.matchedKeyCount)")
logger.debug("- maximumRiskScore: \(summary.maximumRiskScore)")
logger.debug("- riskScoreSumFullRange: \(String(describing: summary.metadata?["riskScoreSumFullRange"]))")
}

func performBackgroundTasks(completionHandler: @escaping (Bool) -> Void) {
debugPrint("performBackgroundTasks")

logger.debug("performBackgroundTasks")

fakeRequestUseCase.sendFalsePositive().subscribe { [weak self] (sent) in
if Config.debug {
@@ -103,13 +109,13 @@ class SetupUseCase: LoggingDelegate, ActivityDelegate, DP3TBackgroundHandler {
body: "Last sync: \(sync), positive sent \(sent)",
sound: .default)
}
completionHandler(sent)
} onError: { (error) in
completionHandler(false)
}


} onError: { [weak self] (error) in
self?.logger.debug("Error sending fake positive \(error) ")
}.disposed(by: disposeBag)

DP3TTracing.delegate = AppDelegate.shared?.injection.resolve(ExpositionUseCase.self)
logger.debug("DP3TTracing.delegate \(String(describing: DP3TTracing.delegate))")
completionHandler(true)
}

func didScheduleBackgrounTask() {
@@ -11,49 +11,50 @@
import Foundation
import UIKit
extension UIColor {

@nonobjc class var powderBlue: UIColor {
return UIColor(red: 211.0 / 255.0, green: 217.0 / 255.0, blue: 224.0 / 255.0, alpha: 1.0)
}

@nonobjc class var algae: UIColor {
return UIColor(red: 88.0 / 255.0, green: 176.0 / 255.0, blue: 106.0 / 255.0, alpha: 1.0)
}

@nonobjc class var darkPeach: UIColor {
return UIColor(red: 224.0 / 255.0, green: 108.0 / 255.0, blue: 87.0 / 255.0, alpha: 1.0)
}

@nonobjc class var cornflower: UIColor {
return UIColor(red: 116.0 / 255.0, green: 148.0 / 255.0, blue: 237.0 / 255.0, alpha: 1.0)
}

@nonobjc class var deepLilac: UIColor {
return UIColor(red: 138.0 / 255.0, green: 124.0 / 255.0, blue: 183.0 / 255.0, alpha: 1.0)
}

@nonobjc class var pale: UIColor {
return UIColor(red: 231.0 / 255.0, green: 228.0 / 255.0, blue: 224.0 / 255.0, alpha: 1.0)
}

@nonobjc class var brownGrey: UIColor {
return UIColor(white: 147.0 / 255.0, alpha: 1.0)
}

@nonobjc class var twilight: UIColor {
return UIColor(red: 112.0 / 255.0, green: 80.0 / 255.0, blue: 156.0 / 255.0, alpha: 1.0)
}

@nonobjc class var white: UIColor {
return UIColor(red: 250.0 / 255.0, green: 249.0 / 255.0, blue: 247.0 / 255.0, alpha: 1.0)
}

@nonobjc class var blueyGrey90: UIColor {
return UIColor(red: 129.0 / 255.0, green: 140.0 / 255.0, blue: 193.0 / 255.0, alpha: 0.9)
extension UIColor {

@nonobjc class var powderBlue: UIColor {
return UIColor(named: "powderBlue") ?? UIColor(red: 211.0 / 255.0, green: 217.0 / 255.0, blue: 224.0 / 255.0, alpha: 1.0)
}

@nonobjc class var purpleApp: UIColor {
return UIColor(red: 73.0 / 255.0, green: 59.0 / 255.0, blue: 167.0 / 255.0, alpha: 0.9)

@nonobjc class var algae: UIColor {
return UIColor(named: "algae") ?? UIColor(red: 88.0 / 255.0, green: 176.0 / 255.0, blue: 106.0 / 255.0, alpha: 1.0)
}

@nonobjc class var darkPeach: UIColor {
return UIColor(named: "darkPeach") ?? UIColor(red: 224.0 / 255.0, green: 108.0 / 255.0, blue: 87.0 / 255.0, alpha: 1.0)
}

@nonobjc class var cornflower: UIColor {
return UIColor(named: "cornflower") ?? UIColor(red: 116.0 / 255.0, green: 148.0 / 255.0, blue: 237.0 / 255.0, alpha: 1.0)
}

@nonobjc class var deepLilac: UIColor {
return UIColor(named: "deepLilac") ?? UIColor(red: 138.0 / 255.0, green: 124.0 / 255.0, blue: 183.0 / 255.0, alpha: 1.0)
}

@nonobjc class var pale: UIColor {
return UIColor(named: "pale") ?? UIColor(red: 231.0 / 255.0, green: 228.0 / 255.0, blue: 224.0 / 255.0, alpha: 1.0)
}

@nonobjc class var brownGrey: UIColor {
return UIColor(named: "brownGrey") ?? UIColor(white: 147.0 / 255.0, alpha: 1.0)
}

@nonobjc class var twilight: UIColor {
return UIColor(named: "twilight") ?? UIColor(red: 112.0 / 255.0, green: 80.0 / 255.0, blue: 156.0 / 255.0, alpha: 1.0)
}

@nonobjc class var white: UIColor {
return UIColor(named: "white") ?? UIColor(red: 250.0 / 255.0, green: 249.0 / 255.0, blue: 247.0 / 255.0, alpha: 1.0)
}

@nonobjc class var degradado: UIColor {
return UIColor(named: "degradado") ?? UIColor(red: 73.0 / 255.0, green: 59.0 / 255.0, blue: 167.0 / 255.0, alpha: 1.0)
}

@nonobjc class var blueyGrey90: UIColor {
return UIColor(red: 129.0 / 255.0, green: 140.0 / 255.0, blue: 193.0 / 255.0, alpha: 0.9)
}
}
@@ -18,8 +18,11 @@ extension UIView {
alpha: CGFloat,
_ message: String? = nil,
_ attributedMessage: NSAttributedString? = nil,
_ textColor: UIColor? = nil
) {
_ textColor: UIColor? = nil,
_ completion: (() -> Void)? = nil,
tagTransparentView: Int = 1111,
tagMenssagetView: Int = 1122) {

let transparentView = Bundle.main.loadNibNamed(
"TransparentView",
owner: self,
@@ -29,7 +32,9 @@ extension UIView {
transparentView?.frame = self.frame
transparentView!.backgroundColor = color
transparentView!.alpha = 0
transparentView!.tag = 1111
transparentView!.tag = tagTransparentView
transparentView!.isAccessibilityElement = true

if let messageView = transparentView?.messageView {
if let regularText = message {
messageView.text = regularText
@@ -40,19 +45,25 @@ extension UIView {
messageView.font = UIFont(name: "Helvetica Neue", size: 26)
messageView.numberOfLines = 0
messageView.minimumScaleFactor = 0.1
messageView.tag = 1122
messageView.tag = tagMenssagetView
messageView.textAlignment = .center
}

DispatchQueue.main.async { [weak self] in
self?.addSubview(transparentView!)
transparentView?.fadeIn(alpha)
transparentView?.fadeIn(alpha, { _ in
if let completion = completion {
completion()
}
})
}
}

func removeTransparentBackGround() {
func removeTransparentBackGround(tagTransparentView: Int = 1111,
tagMenssagetView: Int = 1122) {
DispatchQueue.main.async { [weak self] in
for view in self?.subviews ?? [] {
if view.tag == 1111 || view.tag == 1122 {
if view.tag == tagTransparentView || view.tag == tagMenssagetView {
view.fadeOut { (_) in
view.removeFromSuperview()
}
@@ -110,4 +121,12 @@ extension UIView {
self.alpha = 0.0
}, completion: callBack)
}

func setShadow() {
self.layer.shadowColor = UIColor.powderBlue.cgColor
self.layer.shadowRadius = 3.0
self.layer.shadowOpacity = 0.3
self.layer.shadowOffset = CGSize(width: 2, height: 2)
self.layer.masksToBounds = false
}
}
@@ -42,15 +42,15 @@ extension UIViewController: AlertController {
let uiAlert: UIAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

let action = UIAlertAction(title: buttonTitle, style: .default) { (alert) in
self.view.removeTransparentBackGround()
self.view.removeTransparentBackGround(tagTransparentView: 998, tagMenssagetView: 999)
callback?(alert)
}
uiAlert.addAction(action)
let buttonView = uiAlert.view.subviews.first?.subviews.first?.subviews.first?.subviews[1]
uiAlert.view.tintColor = UIColor.white
buttonView?.backgroundColor = #colorLiteral(red: 0.2, green: 0.1882352941, blue: 0.7254901961, alpha: 1)

self.view.showTransparentBackground(withColor: UIColor.blueyGrey90, alpha: 1)
self.view.showTransparentBackground(withColor: UIColor.blueyGrey90, alpha: 1, tagTransparentView: 998, tagMenssagetView: 999)
self.present(uiAlert, animated: true, completion: nil)
}

@@ -59,7 +59,7 @@ extension UIViewController: AlertController {
let uiAlert: UIAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

let action = UIAlertAction(title: buttonTitle, style: .default) { (alert) in
self.view.removeTransparentBackGround()
self.view.removeTransparentBackGround(tagTransparentView: 998, tagMenssagetView: 999)
callback?(alert)
}
action.isAccessibilityElement = true
@@ -69,7 +69,7 @@ extension UIViewController: AlertController {
uiAlert.view.tintColor = UIColor.white
buttonView?.backgroundColor = #colorLiteral(red: 0.2, green: 0.1882352941, blue: 0.7254901961, alpha: 1)

self.view.showTransparentBackground(withColor: UIColor.blueyGrey90, alpha: 1)
self.view.showTransparentBackground(withColor: UIColor.blueyGrey90, alpha: 1, tagTransparentView: 998, tagMenssagetView: 999)
self.present(uiAlert, animated: true, completion: nil)
}

@@ -87,15 +87,15 @@ extension UIViewController: AlertController {
let uiAlert: UIAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

let action = UIAlertAction(title: buttonOkTitle, style: .default) { (action) in
self.view.removeTransparentBackGround()
self.view.removeTransparentBackGround(tagTransparentView: 998, tagMenssagetView: 999)
okHandler?(action)
}
action.isAccessibilityElement = true
action.accessibilityLabel = buttonOkVoiceover
uiAlert.addAction(action)

let actionCancel = UIAlertAction(title: buttonCancelTitle, style: .default) { (action) in
self.view.removeTransparentBackGround()
self.view.removeTransparentBackGround(tagTransparentView: 998, tagMenssagetView: 999)
cancelHandler?(action)
}
actionCancel.isAccessibilityElement = true
@@ -104,8 +104,8 @@ extension UIViewController: AlertController {
let buttonView = uiAlert.view.subviews.first?.subviews.first?.subviews.first?.subviews[1]
uiAlert.view.tintColor = UIColor.white
buttonView?.backgroundColor = #colorLiteral(red: 0.2, green: 0.1882352941, blue: 0.7254901961, alpha: 1)

self.view.showTransparentBackground(withColor: UIColor.blueyGrey90, alpha: 1)
self.view.showTransparentBackground(withColor: UIColor.blueyGrey90, alpha: 1, tagTransparentView: 998, tagMenssagetView: 999)
self.present(uiAlert, animated: true, completion: nil)

}
@@ -14,6 +14,7 @@ import Foundation
import UIKit
import ExposureNotification
import DP3TSDK
import Logging

protocol ErrorHandler {
func handle(error: Error?)
@@ -102,7 +103,10 @@ class ErrorHandlerImpl: ErrorHandler {
}

class ErrorRecorderImpl: ErrorRecorder {

private let logger = Logger(label: "ErrorRecorderImpl")

func record(error: Error) {
debugPrint("Error \(error)")
logger.error("Error \(error)")
}
}
@@ -16,9 +16,8 @@ protocol ExpositionView {
func userDidTapLabel(tapGestureRecognizer: UITapGestureRecognizer)
}

class BaseExposed: UIViewController, ExpositionView {
class BaseExposed: BaseViewController, ExpositionView {

@IBOutlet weak var backButton: UIButton!
@IBOutlet weak var moreInfoView: UIView!
@IBOutlet weak var expositionBGView: BackgroundView!

@@ -29,7 +28,6 @@ class BaseExposed: UIViewController, ExpositionView {
super.viewDidLoad()

setupBaseView()
setupBaseAccessibility()
}

@IBAction func onBack(_ sender: Any) {
@@ -40,18 +38,6 @@ class BaseExposed: UIViewController, ExpositionView {
//Nothing to do here
}

private func setupBaseAccessibility() {

backButton.isAccessibilityElement = true
let previous = navigationController?.previousViewController
if let title = (previous as? AccTitleView)?.accTitle ?? previous?.title {
backButton.accessibilityLabel = "ACC_BUTTON_BACK_TO".localized + " " + title
} else {
backButton.accessibilityLabel = "ACC_BUTTON_BACK".localized
}
backButton.accessibilityHint = "ACC_HINT".localized
}

private func setupBaseView() {
moreInfoView.isUserInteractionEnabled = true
moreInfoView.addGestureRecognizer(UITapGestureRecognizer(target: self,

Large diffs are not rendered by default.

@@ -41,8 +41,8 @@ class ExpositionViewController: BaseExposed {
private func setupAccessibility() {

titleLabel.isAccessibilityElement = true
titleLabel.accessibilityTraits.insert(UIAccessibilityTraits.header)
titleLabel.accessibilityLabel = "ACC_LOW_EXPOSED_TITLE".localized

titleLabel.accessibilityLabel = "EXPOSITION_LOW_TITLE_2".localized

whatToDoTitleLabel.isAccessibilityElement = true
whatToDoTitleLabel.accessibilityTraits.insert(UIAccessibilityTraits.header)

Large diffs are not rendered by default.

@@ -14,6 +14,7 @@ import RxSwift

class HighExpositionViewController: BaseExposed {

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var whatToDoTitleLabel: UILabel!
@IBOutlet weak var youCouldBeLabel: UILabel!
@@ -26,16 +27,13 @@ class HighExpositionViewController: BaseExposed {
@IBOutlet weak var phoneViewHiddenConstraint: NSLayoutConstraint!

@IBOutlet weak var selectorView: BackgroundView!
@IBOutlet weak var caSelectorLabel: UILabel!
@IBOutlet weak var caSelectorButton: UIButton!
@IBOutlet weak var otherSympthomsLabel: UILabel!
@IBOutlet weak var howActLabel: UILabel!

var ccaUseCase: CCAAUseCase!
var since: Date?
private var ccaArray: [CaData]?
private var currentCA: CaData?

private var pickerPresenter: PickerPresenter?

private let bgImageRed = UIImage(named: "GradientBackgroundRed")
private let bgImageOrange = UIImage(named: "GradientBackgroundOrange")
@@ -44,17 +42,38 @@ class HighExpositionViewController: BaseExposed {

override func viewDidLoad() {
super.viewDidLoad()

self.currentCA = ccaUseCase.getCurrent()


setupView()
setupAccessibility()

setCaSelector()
}

@IBAction func caaSelectAction(_ sender: Any) {
pickerPresenter!.openPicker()
isDisableAccesibility(isDisabble: true)
self.navigationController?.topViewController?.view.showTransparentBackground(withColor: UIColor.blueyGrey90, alpha: 1) {
SelectorView.initWithParentViewController(viewController: self,
title: "LOCALE_SELECTION_REGION_DEFAULT".localized,
getArray:{ [weak self] () -> Observable<[SelectorItem]> in

return Observable.create { [weak self] observer in
self?.ccaUseCase.getCCAA().subscribe(onNext: {(value) in
observer.onNext(SelectorHelperViewModel.generateTransformation(val: value))
observer.onCompleted()
}).disposed(by: self?.disposeBag ?? DisposeBag())
return Disposables.create {
}
}
}, getSelectedItem: { () -> Observable<SelectorItem> in

return Observable.create { [weak self] observer in
observer.onNext(SelectorHelperViewModel.generateTransformation(val: self?.ccaUseCase.getCurrent() ?? CaData.emptyCaData()))
observer.onCompleted()
return Disposables.create {
}
}
}, delegateOutput: self)
}
}

@objc func userDidTapOtherSympthoms(tapGestureRecognizer: UITapGestureRecognizer) {
@@ -68,20 +87,19 @@ class HighExpositionViewController: BaseExposed {
}

@objc func onCallTap(tapGestureRecognizer: UITapGestureRecognizer) {
open(phone: currentCA?.phone ?? "CONTACT_PHONE".localized)
open(phone: ccaUseCase.getCurrent()?.phone ?? "CONTACT_PHONE".localized)
}

@objc override func userDidTapLabel(tapGestureRecognizer: UITapGestureRecognizer) {
onWebTap(tapGestureRecognizer: tapGestureRecognizer, urlString: "MORE_INFO_EXPOSURE_HIGH".localized.getUrlFromHref())
}

@objc func userDidTapWeb(tapGestureRecognizer: UITapGestureRecognizer) {
onWebTap(tapGestureRecognizer: tapGestureRecognizer, urlString: currentCA?.web)
onWebTap(tapGestureRecognizer: tapGestureRecognizer, urlString: ccaUseCase.getCurrent()?.web)
}

private func setupAccessibility() {
titleLabel.isAccessibilityElement = true
titleLabel.accessibilityTraits.insert(UIAccessibilityTraits.header)
titleLabel.accessibilityLabel = "ACC_HIGH_EXPOSED_TITLE".localized

whatToDoTitleLabel.isAccessibilityElement = true
@@ -102,16 +120,18 @@ class HighExpositionViewController: BaseExposed {
howActLabel.accessibilityTraits.insert(UIAccessibilityTraits.link)
howActLabel.accessibilityLabel = "EXPOSITION_HIGH_SYMPTOMS_WHAT_TO_DO".localizedAttributed().string.replacingOccurrences(of: ">", with: "")
howActLabel.accessibilityHint = "ACC_HINT".localized

phoneLabel.accessibilityTraits.insert(UIAccessibilityTraits.button)
phoneLabel.accessibilityHint = "ACC_HINT".localized

covidWebLabel.accessibilityTraits.insert(UIAccessibilityTraits.link)
covidWebLabel.accessibilityHint = "ACC_HINT".localized

caSelectorButton.accessibilityHint = "ACC_HINT".localized
caSelectorButton.accessibilityLabel = self.caSelectorLabel.text
}

private func setupView() {

let picker = UIPickerView.init()
picker.delegate = self
picker.dataSource = self

pickerPresenter = PickerPresenter(picker: picker)
pickerPresenter?.delegate = self

otherSympthomsLabel.isUserInteractionEnabled = true
howActLabel.isUserInteractionEnabled = true
@@ -132,8 +152,6 @@ class HighExpositionViewController: BaseExposed {

phoneView.image = UIImage(named: "WhiteCard")

caSelectorButton.setTitle("LOCALE_SELECTION_REGION_DEFAULT".localized, for: .normal)

//Text Infect
let date = self.since ?? Date()
let formatter = DateFormatter()
@@ -145,85 +163,59 @@ class HighExpositionViewController: BaseExposed {
}
youCouldBeLabel.attributedText = "EXPOSITION_HIGH_DESCRIPTION"
.localizedAttributed(withParams: [String(daysSinceLastInfection), actualizado])

caSelectorLabel.text = "LOCALE_SELECTION_REGION_DEFAULT".localized
caSelectorButton.layer.cornerRadius = 8
caSelectorButton.layer.borderWidth = 1
caSelectorButton.layer.borderColor = UIColor.deepLilac.cgColor
}

private func setCaSelector() {

self.selectorView.image = UIImage.init(named: "WhiteCard")
self.selectorView.isUserInteractionEnabled = true

self.ccaUseCase.getCCAA().subscribe(onNext: { (data) in
self.ccaArray = data
}, onError: { (err) in
print(err)
self.ccaArray = []
}).disposed(by: disposeBag)

guard let currentCa = self.ccaUseCase.getCurrent() else {
self.phoneView.isHidden = true
self.phoneViewHiddenConstraint.priority = .defaultHigh
self.phoneViewVisibleConstraint.priority = .defaultLow
return
}

var temporallyCcaArray: [CaData] = []
temporallyCcaArray.append(currentCa)
temporallyCcaArray += (self.ccaArray ?? []).filter { $0.id != currentCa.id }

self.ccaArray = temporallyCcaArray
self.phoneViewHiddenConstraint.priority = .defaultLow
self.phoneViewVisibleConstraint.priority = .defaultHigh
self.phoneView.isHidden = false
self.phoneLabel.text = currentCa.phone ?? "CONTACT_PHONE".localized
self.covidWebLabel.text = currentCa.webName ?? currentCa.web ?? ""

let title = currentCa.description ?? "LOCALE_SELECTION_REGION_DEFAULT".localized
self.caSelectorButton.setTitle(title, for: .normal)
}
}

extension HighExpositionViewController: UIPickerViewDelegate, UIPickerViewDataSource, PickerDelegate {

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
self.caSelectorLabel.text = title
caSelectorButton.accessibilityLabel = title
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return ccaArray?.count ?? 0
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return ccaArray?[row].description
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
guard let currentCa = self.ccaArray?[row] ?? self.ccaArray?.first else {
return
}
ccaUseCase.setCurrent(cca: currentCa)
self.currentCA = currentCa
}
var containerView: UIView {
get {
view

private func isDisableAccesibility(isDisabble: Bool) {
self.scrollView.isHidden = isDisabble
self.backButton?.isHidden = isDisabble

if let tab = self.parent as? TabBarController {
tab.isDissableAccesibility(isDisabble: isDisabble)
}
}
}

func onDone() {
guard ccaUseCase.getCurrent() != nil else {
// if not current then we need to select the first that was selected
guard let firstca = self.ccaArray?.first else {
setCaSelector()
return
}
ccaUseCase.setCurrent(cca: firstca)
extension HighExpositionViewController: SelectorProtocol {

func userSelectorSelected(selectorItem: SelectorItem, completionCloseView: @escaping (Bool) -> Void) {

if let selectedCaData = selectorItem.objectOrigin as? CaData {
ccaUseCase.setCurrent(cca: selectedCaData)
setCaSelector()
return
}
setCaSelector()

completionCloseView(true)
}

func onCancel() {
//Nothing to do here
func hiddenSelectorSelectionView() {
isDisableAccesibility(isDisabble: false)
}
}
@@ -3,7 +3,7 @@
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -32,6 +32,9 @@
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="¿Qué debo hacer?" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="xcn-at-WWX">
<rect key="frame" x="20" y="294.5" width="374" height="50"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" staticText="YES" header="YES"/>
</accessibility>
<constraints>
<constraint firstAttribute="height" constant="50" id="EpE-Kj-Qr0"/>
</constraints>
@@ -126,6 +129,9 @@
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="DIAGNÓSTICO COMUNICADO" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Fk-UA-jNI">
<rect key="frame" x="26" y="15" width="245" height="21"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" staticText="YES" header="YES"/>
</accessibility>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -311,15 +317,15 @@
<constraint firstAttribute="trailing" secondItem="SjK-xg-WJh" secondAttribute="trailing" id="1iu-hu-mBh"/>
<constraint firstItem="SjK-xg-WJh" firstAttribute="centerX" secondItem="eXl-SL-typ" secondAttribute="centerX" id="Fgu-8q-E3f"/>
<constraint firstItem="SjK-xg-WJh" firstAttribute="top" secondItem="eXl-SL-typ" secondAttribute="top" id="SI6-Tk-jVP"/>
<constraint firstAttribute="bottom" secondItem="SjK-xg-WJh" secondAttribute="bottom" constant="-28" id="m3P-8F-Og4"/>
<constraint firstAttribute="bottom" secondItem="SjK-xg-WJh" secondAttribute="bottom" id="m3P-8F-Og4"/>
<constraint firstItem="SjK-xg-WJh" firstAttribute="width" secondItem="eXl-SL-typ" secondAttribute="width" id="swU-WG-des"/>
</constraints>
<viewLayoutGuide key="contentLayoutGuide" id="OrA-O3-jG3"/>
<viewLayoutGuide key="frameLayoutGuide" id="NFy-hO-4Kq"/>
</scrollView>
</subviews>
<viewLayoutGuide key="safeArea" id="O64-h6-M5h"/>
<color key="backgroundColor" red="0.9804140925" green="0.97666078810000001" blue="0.96814030409999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="bgColor"/>
<constraints>
<constraint firstItem="O64-h6-M5h" firstAttribute="trailing" secondItem="AOX-5N-3Fl" secondAttribute="trailing" constant="20" id="2JF-gL-W8y"/>
<constraint firstAttribute="bottomMargin" secondItem="eXl-SL-typ" secondAttribute="bottom" id="CNA-Xy-oBy"/>
@@ -356,7 +362,7 @@
<image name="iconsBack" width="25.5" height="17"/>
<image name="mask_face" width="42" height="42"/>
<namedColor name="bgColor">
<color red="0.9137254901960784" green="0.9137254901960784" blue="0.9137254901960784" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.98000001907348633" green="0.97600001096725464" blue="0.96899998188018799" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="darkPeach">
<color red="0.87800002098083496" green="0.42399999499320984" blue="0.34099999070167542" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -40,7 +40,7 @@ class PositiveExposedViewController: BaseExposed {

private func setupAccessibility() {
titleLabel.isAccessibilityElement = true
titleLabel.accessibilityTraits.insert(UIAccessibilityTraits.header)

titleLabel.accessibilityLabel = "ACC_POSITIVE_EXPOSED_TITLE".localized

moreInfoLabel.isAccessibilityElement = true
Loading