// // ViewController.swift // Befund // // Created by Irakli Abetschkhrischwili on 29.04.22. // Copyright © 2022 MVZ Dr. Stein und Kollegen. All rights reserved. import AVFoundation import UIKit class ViewController: UIViewController, UIAlertViewDelegate, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate, UNUserNotificationCenterDelegate, UpdatingParentCheckBox { //MARK: *** ViewController public static var CurrentViewController: ViewController? = nil public static var Settings: Core.Models.Settings? = nil public static var currentlySelectedBarcode: String = "" @IBOutlet weak var DashedArea: UIView! @IBOutlet weak var LBLNORESULTS: UILabel! //@IBOutlet weak var LBL_NEW_RESULTS: UILabel! @IBOutlet weak var LBL_MY_RESULTS: UILabel! @IBOutlet weak var LBL_HINWEIS: UILabel! @IBOutlet weak var ResultsTable: UITableView! private var ResultsTableRefresher: UIRefreshControl! @IBOutlet weak var MainLoading: UIActivityIndicatorView! private var dbResults: Array? = nil //MARK: *** PopupNewPGS @IBOutlet weak var PopupNewPGS_Overlay: UIView! @IBOutlet weak var PopupNewPGS: UIView! //MARK: *** PopupNewPGS - PGS @IBOutlet weak var PopupNewPGS_PGS: UIView! @IBOutlet weak var PopupNewPGS_ZipView: UIView! @IBOutlet weak var PopupNewPGS_BirthdayView: UIView! @IBOutlet weak var PopupNewPGS_SampleidView: UIView! @IBOutlet weak var PopupNewPGS_txtZIP: UITextField! @IBOutlet weak var PopupNewPGS_txtBirthday: UITextField! let PopupNewPGS_dtBirthday = UIDatePicker() @IBOutlet weak var PopupNewPGS_txtSampleid: UITextField! @IBOutlet weak var PopupNwePGS_btnScanBarcode: UIButton! @IBOutlet weak var PopupNewPGS_Loading: UIActivityIndicatorView! @IBOutlet weak var PopupNewPGS_Status: UILabel! @IBOutlet weak var PopupNewPGS_labPwdHinweis: UILabel! //MARK: *** PopupNewPGS - MasterPassword @IBOutlet weak var PopupNewPGS_MasterPassword: UIView! @IBOutlet weak var PopupNewPGS_PasswordConfirmView: UIView! @IBOutlet weak var PopupNewPGS_PasswordView: UIView! @IBOutlet weak var PopupNewPGS_txtPassword: UITextField! @IBOutlet weak var PopupNewPGS_txtPasswordConfirm: UITextField! @IBOutlet weak var PopupNewPGS_txtPin: UITextField! @IBOutlet weak var PopupNewPGS_PrivatePolicyArea: UIView! @IBOutlet weak var PopupNewPGS_BtnPrivatePolicy: UIButton! //MARK: *** PopupNewPGS - Buttons @IBOutlet weak var PopupNewPGS_BtnAdd: UIButton! @IBOutlet weak var PopupNewPGS_BtnCancel: UIButton! //MARK: *** PopupPasswordConfirm @IBOutlet weak var PopupPasswordConfirm_Overlay: UIView! @IBOutlet weak var PopupPasswordConfirm: UIView! @IBOutlet weak var PopupPasswordConfirm_BtnCancel: UIButton! @IBOutlet weak var PopupPasswordConfirm_BtnEncrypt: UIButton! @IBOutlet weak var PopupPasswordConfirm_PasswordView: UIView! @IBOutlet weak var PopupPasswordConfirm_txtPassword: UITextField! @IBOutlet weak var PopupPasswordConfirm_Loading: UIActivityIndicatorView! @IBOutlet weak var PopupPasswordConfirm_Status: UILabel! //MARK: *** PopupPrivatePolicy @IBOutlet weak var PopupPrivatePolicy_Overlay: UIView! @IBOutlet weak var PopupPrivatePolicy: UIView! @IBOutlet weak var PopupPrivatePolicy_TextLabel: UILabel! @IBOutlet weak var PopupPrivatePolicy_BtnAccept: UIButton! @IBOutlet weak var PopupPrivatePolicy_HeadLabel: UILabel! // PopupPushNotification @IBOutlet weak var PopupPush_Overlay: UIView! @IBOutlet weak var PopupPush_Content: UIView! @IBOutlet weak var PopupPush_LabelText: UILabel! @IBOutlet weak var PopupPush_LabelHeader: UILabel! @IBOutlet weak var PopupPush_BtnYes: UIButton! @IBOutlet weak var PopupPush_BtnNo: UIButton! //MARK: *** Processing objects private var CurrentDownload: Core.Models.Response.Download? = nil private var CurrentDownloadResults: Core.Models.Database.Results? = nil private var CurrentDownloadProcessing: Bool = false private var LoadingData: Bool = false //MARK: Bottom Menu @IBOutlet weak var BottomMenu: UIView! @IBOutlet weak var launch_Overlay: UIView! @IBOutlet weak var launch_LimbachLogo: UIImageView! @IBOutlet weak var launch_LaborLogo: UIImageView! //private let privatePolicyLink = "https://www.limbachgruppe.com/fileadmin/downloads/6406135946.html" private var afterInitialize: Bool = false @IBOutlet var PopupPrivatePolicy_Checkbox: CheckBoxButton! public var confirmCheckBox: CheckBoxButton! { get { return PopupPrivatePolicy_Checkbox } } override func viewDidLoad() { super.viewDidLoad() self.Initialize() NotificationCenter.default.addObserver(self, selector: #selector(appActivated), name: UIApplication.didBecomeActiveNotification, object: nil) } @objc func appActivated() { if (self.viewIfLoaded?.window != nil && !self.CurrentDownloadProcessing) { self.dashedAreaLongPressed(sender: UILongPressGestureRecognizer()) } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.SetLabels() ViewController.Settings = Core.Models.Settings.loadFromFile(atPath: Core.System.SettingsPath()) if (!self.PopupNewPGS_Overlay.isHidden && ViewController.currentlySelectedBarcode.count > 0) { self.PopupNewPGS_txtSampleid.text = ViewController.currentlySelectedBarcode ViewController.currentlySelectedBarcode = "" } if (self.afterInitialize){ self.afterInitialize = false self.ShowLaborLogo() } else { self.ShowMainView() } } private func ShowMainView() { if(AppDelegate.Session.DevicePassword == nil && (ViewController.Settings?.verificator_hash != nil)) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let devicePwdController = storyboard.instantiateViewController(identifier: "LoginController") devicePwdController.modalPresentationStyle = .fullScreen let transition = CATransition() transition.duration = 0.25 transition.type = .push transition.subtype = .fromRight self.view.window!.layer.add(transition, forKey: kCATransition) self.present(devicePwdController, animated: false) } else { if(ViewController.Settings?.udid != nil) { self.loadResults() } } if(!(ViewController.Settings?.pushNotificationExplained ?? false)) { self.ShowPopup(overlay: self.PopupPush_Overlay, content: self.PopupPush_Content) } else { self.AskForPushPermissions() } } private func ShowLaborLogo() { if ((ViewController.Settings?.labor?.id ?? "").count > 0 ) { self.launch_Overlay.isHidden = false self.launch_LaborLogo.image = ViewController.Settings!.labor!.logo self.launch_LaborLogo.frame.origin = CGPoint(x: -self.view.frame.width, y: self.launch_LimbachLogo.frame.origin.y) self.launch_LaborLogo.isHidden = false let storeX = self.launch_LimbachLogo.frame.origin.x UIView.animate(withDuration: 1.00, animations: { self.launch_LimbachLogo.frame.origin = CGPoint(x: self.view.frame.width, y: self.launch_LimbachLogo.frame.origin.y) self.launch_LaborLogo.frame.origin = CGPoint(x: storeX, y: self.launch_LaborLogo.frame.origin.y) }) {_ in sleep(1) self.launch_Overlay.isHidden = true self.ShowMainView() } } else { self.ShowMainView() } } /** * Initializes view */ private func Initialize() { self.afterInitialize = true //MARK: *** ViewController ViewController.CurrentViewController = self ViewController.Settings = Core.Models.Settings.loadFromFile(atPath: Core.System.SettingsPath()) self.PopupNewPGS_MasterPassword.isHidden = true self.PopupNewPGS_HideLoading() self.launch_Overlay.frame = self.view.frame self.view.addSubview(self.launch_Overlay) //MARK: *** PopupNewPGS self.PopupNewPGS_Overlay.frame = self.view.frame self.view.addSubview(self.PopupNewPGS_Overlay) var popupFieldHeight = (self.PopupNewPGS_PGS.frame.size.height / 3) if(popupFieldHeight > 100) { popupFieldHeight = 100 } let popupFieldMargin = ((popupFieldHeight * 5) / 100) popupFieldHeight = popupFieldHeight - (3 * popupFieldMargin) self.PopupNewPGS_ZipView.frame.size.height = popupFieldHeight self.PopupNewPGS_BirthdayView.frame.size.height = popupFieldHeight self.PopupNewPGS_BirthdayView.frame.origin = CGPoint(x: self.PopupNewPGS_BirthdayView.frame.origin.x, y: self.PopupNewPGS_ZipView.frame.origin.y + self.PopupNewPGS_ZipView.frame.size.height + popupFieldMargin) self.PopupNewPGS_SampleidView.frame.size.height = popupFieldHeight self.PopupNewPGS_SampleidView.frame.origin = CGPoint(x: self.PopupNewPGS_SampleidView.frame.origin.x, y: self.PopupNewPGS_BirthdayView.frame.origin.y + self.PopupNewPGS_BirthdayView.frame.size.height + popupFieldMargin) self.PopupNewPGS_txtZIP.delegate = self self.PopupNewPGS_txtBirthday.delegate = self self.PopupNewPGS_txtSampleid.delegate = self self.PopupNewPGS_txtPassword.delegate = self self.PopupNewPGS_txtPassword.enablePasswordToggle() self.PopupNewPGS_txtPasswordConfirm.delegate = self self.PopupNewPGS_txtPasswordConfirm.enablePasswordToggle() self.PopupNewPGS_txtPin.delegate = self self.PopupNewPGS_txtZIP.returnKeyType = .next self.PopupNewPGS_txtBirthday.returnKeyType = .next self.PopupNewPGS_txtSampleid.returnKeyType = .done self.PopupNewPGS_txtPassword.returnKeyType = .next self.PopupNewPGS_txtPasswordConfirm.returnKeyType = .next self.PopupNewPGS_txtPin.returnKeyType = .done self.PopupPrivatePolicy_Checkbox = CheckBoxButton(frame: CGRect(x: 10, y: 5, width: 30, height: 30)) self.PopupNewPGS_PrivatePolicyArea.addSubview(self.PopupPrivatePolicy_Checkbox) let gesture = UITapGestureRecognizer(target: self, action: #selector(didTapCheckbox)) self.PopupPrivatePolicy_Checkbox.addGestureRecognizer(gesture) self.PopupPrivatePolicy_Checkbox.setChecked(flag: true) self.addDoneToolbar([self.PopupNewPGS_txtZIP, self.PopupNewPGS_txtSampleid, self.PopupNewPGS_txtPassword, self.PopupNewPGS_txtPasswordConfirm, self.PopupNewPGS_txtPin]) self.hideKeyboardWhenTappedAround() self.createDatePicker() //MARK: *** Table self.DashedArea.addDashedBorder() self.ResultsTableRefresher = UIRefreshControl() let attributes = [NSAttributedString.Key.foregroundColor: UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.8), NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15.0)] self.ResultsTableRefresher.attributedTitle = NSAttributedString(string: Core.Lang.Get(key: "MSG_DATA_REFRESHING"), attributes: attributes) self.ResultsTableRefresher.tintColor = .clear self.ResultsTableRefresher.addTarget(self, action: #selector(tableRefresh), for: .valueChanged) self.ResultsTable.addSubview(self.ResultsTableRefresher) self.ResultsTable.isHidden = true self.LBLNORESULTS.isHidden = false self.ResultsTable.delegate = self self.ResultsTable.dataSource = self self.ResultsTable.contentInset = .init(top: 0, left: 0, bottom: 70, right: 0) self.ResultsTable.register(ResultsTableViewCell.nib(), forCellReuseIdentifier: ResultsTableViewCell.identifier) let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(dashedAreaLongPressed)) self.DashedArea.addGestureRecognizer(longPressRecognizer) //MARK: *** PopupPasswordConfirm self.PopupPasswordConfirm_Overlay.isHidden = true self.PopupPasswordConfirm_HideLoading() self.PopupPasswordConfirm_Overlay.frame = self.view.frame self.view.addSubview(self.PopupPasswordConfirm_Overlay) self.PopupPasswordConfirm_txtPassword.delegate = self self.PopupPasswordConfirm_txtPassword.enablePasswordToggle() self.MainLoadingHide() if(ViewController.Settings?.udid != nil) { self.PopupNewPGS_BtnAdd.setTitle(Core.Lang.Get(key: "BTN_ADD"), for: .normal) self.loadResults() } else { self.PopupNewPGS_BtnAdd.setTitle(Core.Lang.Get(key: "BTN_NEXT"), for: .normal) } self.PopupNwePGS_btnScanBarcode.setTitle("", for: .normal) self.PopupPrivatePolicy_Overlay.frame = self.view.frame self.view.addSubview(self.PopupPrivatePolicy_Overlay) self.PopupPrivatePolicy_Overlay.isHidden = true //MARK: Bottom Menu self.BottomMenu.transform = CGAffineTransform(rotationAngle: CGFloat(5.0 * .pi / 180)) self.BottomMenu.frame.origin.y = UIScreen.main.bounds.height self.PopupPush_Overlay.frame = self.view.frame self.view.addSubview(self.PopupPush_Overlay) self.PopupPush_Overlay.isHidden = true self.PopupNewPGS_labPwdHinweis.text = "" self.PopupNewPGS_labPwdHinweis.isHidden = false self.PopupNewPGS_txtPassword.addTarget(self, action: #selector(self.ShowPWDHinweis), for: .editingDidBegin) self.PopupNewPGS_txtPassword.addTarget(self, action: #selector(self.HidePWDHinweis), for: .editingDidEnd) self.PopupNewPGS_txtPasswordConfirm.addTarget(self, action: #selector(self.ShowPWDHinweis), for: .editingDidBegin) self.PopupNewPGS_txtPasswordConfirm.addTarget(self, action: #selector(self.HidePWDHinweis), for: .editingDidEnd) self.PopupNewPGS_txtPin.addTarget(self, action: #selector(self.ShowPinHinweis), for: .editingDidBegin) self.PopupNewPGS_txtPin.addTarget(self, action: #selector(self.HidePWDHinweis), for: .editingDidEnd) } @objc func ShowPWDHinweis() { self.PopupNewPGS_Status.text = "" self.PopupNewPGS_labPwdHinweis.text = Core.Lang.Get(key: "ERROR_ENTER_STRONG_PASSWORD") self.PopupNewPGS_labPwdHinweis.isHidden = false } @objc func ShowPinHinweis() { self.PopupNewPGS_Status.text = "" self.PopupNewPGS_labPwdHinweis.text = Core.Lang.Get(key: "ERROR_PIN_LENGTH") self.PopupNewPGS_labPwdHinweis.isHidden = false } @objc func HidePWDHinweis() { self.PopupNewPGS_labPwdHinweis.text = "" self.PopupNewPGS_labPwdHinweis.isHidden = true } private func SetLabels() { // Initialize Language self.LBL_MY_RESULTS.text = Core.Lang.Get(key: "LBL_MY_RESULTS") self.LBL_HINWEIS.text = Core.Lang.Get(key: "LBL_BEFUND_HINWEIS") self.PopupNewPGS_txtZIP.placeholder = Core.Lang.Get(key: "LBL_ZIP") self.PopupNewPGS_txtBirthday.placeholder = Core.Lang.Get(key: "LBL_BIRTHDAY") self.PopupNewPGS_txtSampleid.placeholder = Core.Lang.Get(key: "LBL_SAMPLEID") self.PopupNewPGS_txtPassword.placeholder = Core.Lang.Get(key: "LBL_PASSWORD") self.PopupNewPGS_txtPasswordConfirm.placeholder = Core.Lang.Get(key: "LBL_PASSWORD_CONFIRM") self.PopupNewPGS_BtnCancel.setTitle(Core.Lang.Get(key: "BTN_CANCEL"), for: .normal) self.PopupNewPGS_BtnAdd.setTitle(Core.Lang.Get(key: "BTN_ADD"), for: .normal) self.PopupNewPGS_BtnPrivatePolicy.setTitle(Core.Lang.Get(key: "LBL_PRIVACY_POLICY"), for: .normal) self.LBLNORESULTS.text = Core.Lang.Get(key: "LBL_NO_RESULTS") self.PopupPasswordConfirm_BtnCancel.setTitle(Core.Lang.Get(key: "BTN_CANCEL"), for: .normal) self.PopupPasswordConfirm_BtnEncrypt.setTitle(Core.Lang.Get(key: "BTN_ENCRYPT"), for: .normal) self.PopupPasswordConfirm_txtPassword.placeholder = Core.Lang.Get(key: "LBL_PASSWORD") self.PopupPrivatePolicy_TextLabel.text = Core.Lang.Get(key: "PRIVATE_POLICY_TEXT") self.PopupPrivatePolicy_HeadLabel.text = Core.Lang.Get(key: "LBL_PRIVACY_POLICY") self.PopupPrivatePolicy_BtnAccept.setTitle(Core.Lang.Get(key: "BTN_ACCEPT_PRIVACY_POLICY"), for: .normal) self.PopupPush_LabelHeader.text = Core.Lang.Get(key: "LBL_PUSH_NOTIFICATION_EXPLAIN_HEADER") self.PopupPush_LabelText.text = Core.Lang.Get(key: "MSG_PUSH_NOTIFICATION_EXPLAIN_TEXT") self.PopupPush_BtnYes.setTitle(Core.Lang.Get(key: "BTN_OK"), for: .normal) } @objc func didTapCheckbox() { self.PopupPrivatePolicy_Checkbox.toogle() } /** * Creates the datepicker on the bottom popup menu */ func createDatePicker() { let formatter=DateFormatter() formatter.dateFormat = "yyyy/MM/dd" self.PopupNewPGS_dtBirthday.locale = Locale(identifier: Core.Lang.GetLocale()) self.PopupNewPGS_dtBirthday.calendar.locale = Locale(identifier: Core.Lang.GetLocale()) self.PopupNewPGS_dtBirthday.datePickerMode = .date self.PopupNewPGS_dtBirthday.preferredDatePickerStyle = .wheels self.PopupNewPGS_dtBirthday.date = formatter.date(from: "2000/01/01")! self.PopupNewPGS_dtBirthday.maximumDate = Date() self.PopupNewPGS_dtBirthday.minimumDate = formatter.date(from: "1900/01/01") let toolbar = UIToolbar() let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let doneButton = UIBarButtonItem(title: Core.Lang.Get(key: "BTN_DONE"), style: .done, target: self, action: #selector(PopupNewPGS_dtBirthdayDone)) toolbar.setItems([flexSpace, doneButton], animated: true) toolbar.sizeToFit() self.PopupNewPGS_txtBirthday.inputAccessoryView = toolbar self.PopupNewPGS_txtBirthday.inputView = self.PopupNewPGS_dtBirthday } //MARK: BEGIN Application Event Bridgs public func onSessionChanged(session: Core.Models.Session) { //TODO: handle if push permission was taken away-> Show Instruction View that the user has to permitt permissions to push } //MARK: END Application Event Bridgs //MARK: BEGIN Events @objc func PopupNewPGS_dtBirthdayDone() { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "dd.MM.yyyy" let dt = dateFormatter.string(from: self.PopupNewPGS_dtBirthday.date) self.PopupNewPGS_txtBirthday.text = dt self.PopupNewPGS_txtSampleid.becomeFirstResponder() } @IBAction func popupPush_BtnYes_Click(_ sender: Any) { self.PushNotificationAccepted(true) } private func PushNotificationAccepted(_ flagAccepted: Bool) { if (ViewController.Settings == nil) { ViewController.Settings = Core.Models.Settings() } ViewController.Settings!.pushNotificationExplained = true ViewController.Settings!.pushNotificationAccepted = flagAccepted _ = ViewController.Settings!.save(atPath: Core.System.SettingsPath()) self.HidePopup(overlay: self.PopupPush_Overlay, content: self.PopupPush_Content) if(flagAccepted) { self.AskForPushPermissions() } } private func OpenScanner2SelectLabor(_ sender: Any) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let scannerController = storyboard.instantiateViewController(identifier: "ScannerController") scannerController.modalPresentationStyle = .fullScreen let transition = CATransition() transition.duration = 0.25 transition.type = .push transition.subtype = .fromRight self.view.window!.layer.add(transition, forKey: kCATransition) self.present(scannerController, animated: false) } @IBAction func ScanBarcodeClick(_ sender: Any) { let scannerController = ScannerViewController.InitScannerController(scanSampleID: true) scannerController.modalPresentationStyle = .fullScreen let transition = CATransition() transition.duration = 0.25 transition.type = .push transition.subtype = .fromRight self.view.window!.layer.add(transition, forKey: kCATransition) self.present(scannerController, animated: false) } // MARK: ON AddButton Click @IBAction func AddButtonClick(_ sender: Any) { self.PopupNewPGS_Status.text = "" self.PopupNewPGS_txtSampleid.text = "" self.PopupNewPGS_Loading.isHidden = true self.PopupNewPGS_Loading.stopAnimating() self.PopupNewPGS_BtnCancel.setTitle(Core.Lang.Get(key: "BTN_CANCEL"), for: .normal) if(ViewController.Settings?.udid != nil) { if(ViewController.Settings!.zip != nil) { self.PopupNewPGS_txtZIP.text = ViewController.Settings!.zip } if(ViewController.Settings!.birthday != nil) { self.PopupNewPGS_dtBirthday.date = ViewController.Settings!.GetBirthday()! self.PopupNewPGS_txtBirthday.text = ViewController.Settings!.GetFormatedBirthday()! } self.PopupNewPGS_BtnAdd.setTitle(Core.Lang.Get(key: "BTN_ADD"), for: .normal) self.PopupNewPGS_PGS.frame.origin = CGPoint(x: 0, y: self.PopupNewPGS_PGS.frame.origin.y) self.PopupNewPGS_MasterPassword.frame.origin = CGPoint( x: self.PopupNewPGS_MasterPassword.frame.width, y: self.PopupNewPGS_MasterPassword.frame.origin.y) self.PopupNewPGS_MasterPassword.isHidden = true } else { self.PopupNewPGS_BtnAdd.setTitle(Core.Lang.Get(key: "BTN_NEXT"), for: .normal) } self.PopupNewPGS.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) self.PopupNewPGS_Overlay.alpha = 0.0 self.PopupNewPGS_Overlay.isHidden = false UIView.animate(withDuration: 0.24) { self.PopupNewPGS.transform = CGAffineTransform.identity self.PopupNewPGS_Overlay.alpha = 70.0 } } private func ShowPopup(overlay: UIView!, content: UIView!) { content.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) overlay.alpha = 0.0 overlay.isHidden = false UIView.animate(withDuration: 0.24) { content.transform = CGAffineTransform.identity overlay.alpha = 70.0 } } private func HidePopup(overlay: UIView!, content: UIView!) { UIView.animate(withDuration: 0.24, animations: { overlay.alpha = 0.0 content.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) }) {_ in overlay.isHidden = true } } private func Ask4PolicyAGBFirstTime() { if(!(ViewController.Settings?.policyAGBExplained ?? false)) { self.ShowPrivatePolicyInWeb() } } private func AskForPushPermissions() { let center = UNUserNotificationCenter.current() center.delegate = self center.requestAuthorization(options: [ .sound,.alert,.badge ]) { (granted, error) in if error == nil { if (granted) { AppDelegate.Session.NotificationGranted = true DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } else { AppDelegate.Session.NotificationGranted = false Core.Log.Warning(msg: "User did not grant the permission to the notifications", namespace: "AppDelegate", method: "Initialize") } DispatchQueue.main.async { self.Ask4PolicyAGBFirstTime() } } else { AppDelegate.Session.NotificationGranted = false Core.Log.Error(err: error!, namespace: "AppDelegate", method: "Initialize") } } } // MARK: BEGIN PUSH-Notification delegates func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (_ options: UNNotificationPresentationOptions) -> Void) { if(ViewController.CurrentViewController != nil) { ViewController.CurrentViewController!.loadResults() } } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { UIApplication.shared.applicationIconBadgeNumber = 1 } // MARK: END PUSH-Notification delegates //MARK: BEGIN TextField delegates func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let currentString: NSString = textField.text! as NSString let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString if(textField == self.PopupNewPGS_txtSampleid) { let filteredString = string.rangeOfCharacter(from: NSCharacterSet.alphanumerics) return newString.length <= 14 && (filteredString != nil || string.count == 0) } if(textField == self.PopupNewPGS_txtZIP || textField == self.PopupNewPGS_txtPin) { let filteredString = string.rangeOfCharacter(from: NSCharacterSet.decimalDigits) return newString.length <= 5 && (filteredString != nil || string.count == 0) } return true } func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { self.PopupNewPGS_Status.text = "" if(textField == self.PopupNewPGS_txtSampleid) { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "dd.MM.yyyy" let dt = dateFormatter.string(from: self.PopupNewPGS_dtBirthday.date) self.PopupNewPGS_txtBirthday.text = dt } return true } func textFieldShouldReturn(_ textField: UITextField) -> Bool { if(textField == self.PopupNewPGS_txtZIP) { self.PopupNewPGS_txtBirthday.becomeFirstResponder() } else if(textField == self.PopupNewPGS_txtBirthday) { self.PopupNewPGS_txtSampleid.becomeFirstResponder() } else if(textField == self.PopupNewPGS_txtSampleid) { if(ViewController.Settings?.verificator_hash == nil) { self.showPasswordArea() self.PopupNewPGS_txtPassword.becomeFirstResponder() } else { self.view.endEditing(true) self.requestPGS() } } else if(textField == self.PopupNewPGS_txtPassword) { self.PopupNewPGS_txtPasswordConfirm.becomeFirstResponder() } else if(textField == self.PopupNewPGS_txtPasswordConfirm) { self.PopupNewPGS_txtPin.becomeFirstResponder() } else if(textField == self.PopupNewPGS_txtPin) { self.view.endEditing(true) self.requestPGS() } return true } //MARK: END TextField delegates @IBAction func PopupNewPGS_BtnCancelClick(_ sender: Any) { self.view.endEditing(true) if(!self.PopupNewPGS_Loading.isAnimating) { if(self.PopupNewPGS_MasterPassword.isHidden) { self.PopupNewPGSHide() } else { self.hidePasswordArea() } } } private func PopupNewPGSHide() { UIView.animate(withDuration: 0.24, animations: { self.PopupNewPGS_Overlay.alpha = 0.0 self.PopupNewPGS.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) }) {_ in self.PopupNewPGS_Overlay.isHidden = true } } @IBAction func PopupMenuArea_BtnSettingsClick(_ sender: Any) { self.view.endEditing(true) //self.PopupMenuAreaHide() let storyboard = UIStoryboard(name: "Main", bundle: nil) let settingsController = storyboard.instantiateViewController(identifier: "SettingsController") settingsController.modalPresentationStyle = .fullScreen let transition = CATransition() transition.duration = 0.25 transition.type = .push transition.subtype = .fromRight self.view.window!.layer.add(transition, forKey: kCATransition) self.present(settingsController, animated: false) } @IBAction func PopupNewPGS_BtnAddClick(_ sender: Any) { if(ViewController.Settings?.udid != nil) { self.requestPGS() } else { if(self.validatePGSData()) { self.showPasswordArea() } } } @objc func dashedAreaLongPressed(sender: UILongPressGestureRecognizer) { if(ViewController.Settings?.udid != nil) { self.loadResults() } } //MARK: END Events //MARK: BEGIN Functions private func showPasswordArea() { if(self.PopupNewPGS_MasterPassword.isHidden) { self.PopupNewPGS_MasterPassword.frame.origin = CGPoint(x: self.PopupNewPGS_MasterPassword.frame.width, y: self.PopupNewPGS_MasterPassword.frame.origin.y) self.PopupNewPGS_MasterPassword.isHidden = false UIView.animate(withDuration: 0.24, animations: { self.PopupNewPGS_PGS.frame.origin = CGPoint(x: -self.PopupNewPGS_PGS.frame.width, y: self.PopupNewPGS_PGS.frame.origin.y) self.PopupNewPGS_MasterPassword.frame.origin = CGPoint(x: 0, y: self.PopupNewPGS_MasterPassword.frame.origin.y) }) {_ in self.PopupNewPGS_BtnCancel.setTitle(Core.Lang.Get(key: "BTN_BACK"), for: .normal) self.PopupNewPGS_BtnAdd.setTitle(Core.Lang.Get(key: "BTN_REGISTER"), for: .normal) } } else { self.requestPGS() } } @IBAction func PopupPrivatePolicy_BtnAcceptClick(_ sender: Any) { self.HidePrivatePolicy() } @IBAction func PopupPrivatePolicy_BtnAddClick(_ sender: Any) { self.ShowPrivatePolicyInWeb() } private func getLocalPolicyLink() -> String { let baseURL = Bundle.main.path(forResource: "privacy_policy", ofType: "html") return baseURL ?? "" } private func ShowPrivatePolicyInWeb() { guard let url = URL(string: Core.Lang.Get(key: "AGB_POLICY_LINK")) else { return } let localUrl = URL(string: getLocalPolicyLink()) ?? url let vc = WebViewController(url: url, title: Core.Lang.Get(key: "BTN_ACCEPT_PRIVACY_POLICY"), localUrl: localUrl) vc.parentView = self let navVC = UINavigationController(rootViewController: vc) present(navVC, animated: true) } private func ShowPrivatePolicy() { self.PopupPrivatePolicy_Overlay.alpha = 100 self.PopupPrivatePolicy_Overlay.isHidden = false UIView.animate(withDuration: 0.24, animations: { self.PopupNewPGS_Overlay.frame.origin = CGPoint(x: -self.PopupNewPGS_Overlay.frame.width, y: self.PopupNewPGS_Overlay.frame.origin.y) self.PopupPrivatePolicy.frame.origin = CGPoint(x: 0, y: self.PopupPrivatePolicy.frame.origin.y) }) } private func HidePrivatePolicy() { self.PopupPrivatePolicy_Checkbox.setChecked(flag: true) UIView.animate(withDuration: 0.24, animations: { self.PopupNewPGS_Overlay.frame.origin = CGPoint(x: 0, y: self.PopupNewPGS_Overlay.frame.origin.y) self.PopupPrivatePolicy.frame.origin = CGPoint(x: self.PopupPrivatePolicy.frame.width, y: self.PopupPrivatePolicy.frame.origin.y) }) {_ in self.PopupPrivatePolicy_Overlay.isHidden = true self.PopupNewPGS_Overlay.isHidden = false } } private func hidePasswordArea() { UIView.animate(withDuration: 0.24, animations: { self.PopupNewPGS_PGS.frame.origin = CGPoint(x: 0, y: self.PopupNewPGS_PGS.frame.origin.y) self.PopupNewPGS_MasterPassword.frame.origin = CGPoint(x: self.PopupNewPGS_MasterPassword.frame.width, y: self.PopupNewPGS_MasterPassword.frame.origin.y) }) {_ in self.PopupNewPGS_MasterPassword.isHidden = true self.PopupNewPGS_BtnCancel.setTitle(Core.Lang.Get(key: "BTN_CANCEL"), for: .normal) self.PopupNewPGS_BtnAdd.setTitle(Core.Lang.Get(key: "BTN_NEXT"), for: .normal) } } /** * Load results from the database and from the outside service * @param onlyLocale - if true it will load only from locale db */ public func loadResults(onlyLocale: Bool = false) { //if password is not defined than don't make any results loading if (AppDelegate.Session.DevicePassword == nil) { return } if(!self.LoadingData) { self.LoadingData = true self.dbResults = Core.Database.Results.GetResults() let resultsAvailable = self.dbResults != nil && self.dbResults!.count > 0 self.LBLNORESULTS.isHidden = resultsAvailable self.ResultsTable.isHidden = !resultsAvailable self.ResultsTable.reloadData() UIApplication.shared.applicationIconBadgeNumber = 0 if(!onlyLocale) { self.MainLoadingShow() //MARK: Load from server https://stackoverflow.com/questions/24190277/writing-handler-for-uialertaction let getOpened = Core.Models.Request.GetOpened() getOpened.udid = AppDelegate.Session.Device!.udid getOpened.verificator_hash = ViewController.Settings!.verificator_hash let requestKeyPair = Core.Security.Curve25519.GenerateKeyPair() let currentHost = (ViewController.Settings!.labor?.host ?? .DEVELOPMENT) Core.Https.Request.KeyExchangeAsync(host: currentHost, keyPair: requestKeyPair, onSuccess: { publicKey in let sharedKey = requestKeyPair.GetSharedKey(peerPublicKeyBase64: publicKey.key) let encryptedRequest = Core.Models.Request.EncryptedRequest(descriptor: "GetOpened", contentObject: getOpened, requestType: .REQUEST_GET_OPENED, key: sharedKey!) Core.Https.Request.EncryptedRequestAsync(host: currentHost, controller: "results", action: "opened", request: encryptedRequest, serverPublicKey: publicKey, keyPair: requestKeyPair, onSuccess: { encryptedResponse in let status = encryptedResponse.Decrypt(key: sharedKey!) as? Array self.dbResults = Core.Database.Results.GetResults(activeOnly: false) if(status != nil && status!.count > 0) { //MARK: Check if new requests or update for i in 0...(status!.count-1) { let s = status![i] let statusText = s.results_status?.lowercased() ?? "" let results = s.ToResults() if(statusText.count > 0 && statusText != "none") { if(statusText == "not_found") { _ = Core.Database.Results.Delete(results: results) DispatchQueue.main.async { self.ShowMessagePopup(title: "", message: Core.Lang.Get(key: "MSG_REQUESTED_RESULTS_NOT_FOUND")) } } else if (statusText == "rejected") { DispatchQueue.main.async { self.ShowMessagePopup(title: "", message: Core.Lang.Get(key: "MSG_REQUESTED_RESULTS_REJECTED")) } } else { if(!self.dbResults!.contains(where: { $0.pgs == s.pgs })) { if(!Core.Database.Results.Create(results: results)) { Core.Log.Error(msg: "could not create results in db", namespace: "ViewController", method: "loadResults") } } else { if(!Core.Database.Results.Update(results: results)) { Core.Log.Error(msg: "could not update results in db", namespace: "ViewController", method: "loadResults") } } self.dbResults = Core.Database.Results.GetResults() DispatchQueue.main.async { let resultsAvailable = self.dbResults != nil && self.dbResults!.count > 0 self.LBLNORESULTS.isHidden = resultsAvailable self.ResultsTable.isHidden = !resultsAvailable self.ResultsTable.reloadData() } } } } } DispatchQueue.main.async { self.MainLoadingHide() } self.LoadingData = false }, onError: { error in Core.Log.Error(msg: error, namespace: "ViewController", method: "loadResults") self.LoadingData = false DispatchQueue.main.async { self.MainLoadingHide() } }) }, onError: { error in self.LoadingData = false Core.Log.Error(msg: error, namespace: "ViewController", method: "loadResults") DispatchQueue.main.async { if(AppDelegate.Session.Maintenance && !AppDelegate.Session.MaintenanceNotified) { AppDelegate.Session.MaintenanceNotified = true self.ShowMessagePopup(title: "Fehler", message: Core.Lang.Get(key: "MSG_MAINTENANCE")) } self.MainLoadingHide() } }) } else { self.LoadingData = false } } } private func validatePGSData() -> Bool { if(!self.PopupNewPGS_Loading.isAnimating) { self.PopupNewPGS_ShowLoading() let zipText = self.PopupNewPGS_txtZIP.text ?? "" let birthdayText = self.PopupNewPGS_txtBirthday.text ?? "" let sampleidText = self.PopupNewPGS_txtSampleid.text ?? "" if(zipText.count==0) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_ZIP")) //self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ALREADY_SUBSCRIBTED")) } else if(birthdayText.count == 0) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_BIRTHDAY")) } else if(sampleidText.count == 0) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_SAMPLEID")) } else if(zipText.count < 4) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_VALID_ZIP")) } else if(birthdayText.count < 10 ) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_VALID_BIRTHDAY")) } else if(sampleidText.count < 6 || sampleidText.count > 14) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_VALID_SAMPLEID")) } else { self.PopupNewPGS_HideLoading() return true } } return false } private func validateRegistrationData() -> Bool { if(!self.PopupNewPGS_Loading.isAnimating) { self.PopupNewPGS_ShowLoading() let pinText = self.PopupNewPGS_txtPin.text ?? "" let passwordText = self.PopupNewPGS_txtPassword.text ?? "" if(passwordText.count == 0) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_PASSWORD")) } else { if(!Core.Models.Request.ChangeVerificatorHashProvider.IsPasswordStrong(password: passwordText)) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_STRONG_PASSWORD")) } else if(self.PopupNewPGS_txtPassword.text != self.PopupNewPGS_txtPasswordConfirm.text) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_PASSWORD_MISMATCH")) } else if(pinText.count == 0) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_PIN")) } else if(pinText.count != 5) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_PIN_LENGTH")) } else if(!self.PopupPrivatePolicy_Checkbox.IsChecked()) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ACCEPT_PRIVACY_POLICY")) } else { self.PopupNewPGS_HideLoading() return true } } } return false } /** * Send PGS Request to the server */ private func requestPGS() { self.PopupNewPGS_Status.text = "" let zipText = self.PopupNewPGS_txtZIP.text ?? "" let birthdayText = self.PopupNewPGS_txtBirthday.text ?? "" let sampleidText = self.PopupNewPGS_txtSampleid.text ?? "" let pinText = self.PopupNewPGS_txtPin.text ?? "" let passwordText = self.PopupNewPGS_txtPassword.text ?? "" if(self.validatePGSData()) { //First installation if(ViewController.Settings?.udid == nil) { if(self.validateRegistrationData()) { self.PopupNewPGS_ShowLoading() let encryptionKeyPair = Core.Security.Curve25519.GenerateKeyPair() if(encryptionKeyPair.PrivateKey != nil && encryptionKeyPair.PublicKey != nil) { if (ViewController.Settings == nil) { ViewController.Settings = Core.Models.Settings() } AppDelegate.Session.DevicePassword = passwordText let pwdKey = Core.Security.AES.GetKey(password: passwordText) let pwdKeyString = String(decoding: pwdKey, as: UTF8.self) ViewController.Settings!.public_key = encryptionKeyPair.PublicKey! let pwd_reset_hash = NSUUID().uuidString ViewController.Settings!.password_reset_hash = pwd_reset_hash let passwordHashKey = Core.Security.AES.GetKey(password: pwd_reset_hash + pinText) let pwdHash = Core.Security.AES.Encrypt(value: passwordText, password: String(decoding: passwordHashKey, as: UTF8.self)) ViewController.Settings!.password_hash = pwdHash ViewController.Settings!.verificator_hash = Core.Security.SHA512.HMAC(message: Core.Security.SHA512.VerificatorHashingValue.data(using: .utf8)!, key: pwdKeyString) ViewController.Settings!.hashed_private_key = Core.Security.AES.Encrypt(value: encryptionKeyPair.PrivateKey!, password: pwdKeyString) ViewController.Settings!.zip = zipText ViewController.Settings!.SetBirthday(date: PopupNewPGS_dtBirthday.date) ViewController.Settings!.udid = AppDelegate.Session.Device!.udid if(!ViewController.Settings!.save(atPath: Core.System.SettingsPath())) { PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_SAVE")) Core.Log.Critical(msg: "Could not save settings to the file", namespace: "ViewController", method: "requestPGS") } else { ViewController.Settings = Core.Models.Settings.loadFromFile(atPath: Core.System.SettingsPath()) } } else { PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ENCRYPTION_NOT_SUPPORTED")) Core.Log.Critical(msg: "Could not create Curve25519 keyPair", namespace: "ViewController", method: "requestPGS") } } } else { ViewController.Settings!.zip = zipText ViewController.Settings!.SetBirthday(date: PopupNewPGS_dtBirthday.date) if(!ViewController.Settings!.save(atPath: Core.System.SettingsPath())) { PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_SAVE")) Core.Log.Critical(msg: "Could not save settings to the file", namespace: "ViewController", method: "requestPGS") } } // User has password already defined if(ViewController.Settings?.udid != nil) { self.PopupNewPGS_ShowLoading() let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let birthday = dateFormatter.string(from: self.PopupNewPGS_dtBirthday.date) let subscribe = Core.Models.Request.Subscribe() subscribe.udid = AppDelegate.Session.Device!.udid subscribe.device_token = AppDelegate.Session.Device!.token subscribe.pgs = Core.Security.SHA512.Encrypt(data: (zipText + birthday + sampleidText).data(using: .utf8)!) let pwd = Core.Security.AES.GetKey(password: subscribe.udid! + Core.Security.AES.PGS_ENCRYPT_PARTIAL_KEY) subscribe.pgs_hash = Core.Security.AES.Encrypt(value: (zipText + "|" + birthday + "|" + sampleidText), password: String(decoding:pwd, as: UTF8.self))! subscribe.client_public_key = ViewController.Settings!.public_key subscribe.device_type = 1 subscribe.verificator_hash = ViewController.Settings!.verificator_hash subscribe.pin = pinText if(subscribe.pin?.isEmpty ?? true) { subscribe.pin = String(Int.random(in: 10000..<99999)) } let dbResults = Core.Database.Results.GetResults(pgs: subscribe.pgs!) if(dbResults != nil) { let status = dbResults?.GetStatus() if(status == .COMPLETED) { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "MSG_PGS_ALREADY_EXISTS")) } else { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "MSG_PGS_RESPONSE_PENDING")) } } else { self.hideKeyboard() let requestKeyPair = Core.Security.Curve25519.GenerateKeyPair() let currentHost = (ViewController.Settings!.labor?.host ?? .DEVELOPMENT) Core.Https.Request.KeyExchangeAsync(host: currentHost, keyPair: requestKeyPair, onSuccess: { publicKey in let sharedKey = requestKeyPair.GetSharedKey(peerPublicKeyBase64: publicKey.key) let encryptedRequest = Core.Models.Request.EncryptedRequest(descriptor: "Subscribe", contentObject: subscribe, requestType: .REQUEST_SUBSCRIBE, key: sharedKey!) Core.Https.Request.EncryptedRequestAsync(host: currentHost, controller: "results", action: "subscribe", request: encryptedRequest, serverPublicKey: publicKey, keyPair: requestKeyPair, onSuccess: { encryptedResponse in let response = encryptedResponse.Decrypt(key: sharedKey!) DispatchQueue.main.async { if(encryptedResponse.descriptor!.lowercased() == "success") { if(Core.Database.Results.Create(results: subscribe.ToResults())) { self.PopupNewPGS_HideLoading() self.ShowMessagePopup(title: "Vielen Dank", message: Core.Lang.Get(key: "MSG_PGS_SUCCESSFULLY_CREATED")) self.loadResults() DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.PopupNewPGSHide() } } else { self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_SAVE")) Core.Log.Critical(msg: "Could not save results to the database", namespace: "ViewController", method: "requestPGS") } } else if(encryptedResponse.descriptor!.lowercased() == "status") { let status = (response as? Core.Models.Database.Status) if(status == nil || !Core.Database.Results.CreateOrUpdate(results: status!.ToResults())) { Core.Log.Critical(msg: "Could not update status in the database", namespace: "ViewController", method: "requestPGS") } self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "MSG_PGS_ALREADY_AVAILABLE")) self.loadResults() DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.PopupNewPGSHide() } } else { let responseException = (response as? Core.Models.Response.Exception) if(responseException != nil) { switch(responseException!.error_type) { case Core.Models.Response.Exception.Types.MaxOpenedRequestLimit : self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_MAX_OPENED_REQUEST_LIMIT")) case Core.Models.Response.Exception.Types.AlreadySubscribted: self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_ALREADY_SUBSCRIBTED")) default: self.PopupNewPGS_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_REGISTER_PGS")) } } else { self.OnErrorLoading(error: Core.Lang.Get(key: "ERROR_COULD_NOT_REGISTER_PGS"), logmsg: "Could not register", method: "requestPGS") } } } }, onError: self.OnErrorHandler) }, onError: self.OnErrorHandler); } } } } private func OnErrorHandler(error: String)->Void { self.OnErrorLoading(error: error, logmsg: "Server not reachable", method: "requestPGS") } private func OnErrorLoading(error: String, logmsg: String, method: String) { DispatchQueue.main.async { self.PopupNewPGS_HideLoading(message: error) Core.Log.Critical(msg: logmsg, namespace: "ViewController", method: method) } } private func PopupNewPGS_ShowLoading() { self.PopupNewPGS_Loading.isHidden = false self.PopupNewPGS_Loading.startAnimating() self.PopupNewPGS_Status.text = "" self.PopupNewPGS_Status.isHidden = true } private func PopupNewPGS_HideLoading(message: String? = nil) { self.PopupNewPGS_Loading.isHidden = true self.PopupNewPGS_Loading.stopAnimating() if(message != nil) { self.PopupNewPGS_Status.text = message! self.PopupNewPGS_Status.isHidden = false } } private func PopupPasswordConfirm_ShowLoading() { self.PopupPasswordConfirm_Loading.isHidden = false self.PopupPasswordConfirm_Loading.startAnimating() self.PopupPasswordConfirm_Status.text = "" self.PopupPasswordConfirm_Status.isHidden = true } private func PopupPasswordConfirm_HideLoading(message: String? = nil) { self.PopupPasswordConfirm_Loading.isHidden = true self.PopupPasswordConfirm_Loading.stopAnimating() if(message != nil) { self.PopupPasswordConfirm_Status.text = message! self.PopupPasswordConfirm_Status.isHidden = false } } private func MainLoadingHide() { self.MainLoading.stopAnimating() self.MainLoading.isHidden = true self.ResultsTableRefresher.endRefreshing() } private func MainLoadingShow() { self.MainLoading.startAnimating() self.MainLoading.isHidden = false } //MARK: END Functions //MARK: BEGIN TABLE FUNCTIONS @objc func tableRefresh(_ sender: Any) { self.loadResults() } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 70.0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: ResultsTableViewCell.identifier, for: indexPath) as! ResultsTableViewCell if(self.dbResults != nil && self.dbResults!.count > 0 && indexPath.row >= 0 && indexPath.row < self.dbResults!.count) { let result = self.dbResults![indexPath.row] cell.title.text = result.GetFormatedCreatedDate() cell.subtitle.text = result.GetStatusText() cell.identifier.text = result.pgs! cell.icon.setImage(result.GetStatusIcon(), for: .normal) cell.icon.tag = indexPath.row if(cell.icon.allTargets.count > 0) { cell.icon.removeTarget(nil, action: nil, for: .allEvents) } cell.icon.addTarget(self, action: #selector(onTableRowIconClick), for: .touchUpInside) cell.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) cell.icon.isUserInteractionEnabled = true cell.selectionStyle = .default; let bgView = UIView() bgView.backgroundColor = .clear cell.selectedBackgroundView = bgView // MARK: Very dirty to start auto download here but for now no chance to do other way // because we need to change the icon of the row if(result.GetStatus() == .COMPLETED && result.available && !result.picked_up && !self.CurrentDownloadProcessing && AppDelegate.Session.Device!.token != nil) { let gifLoading = UIImage.gifImageWithName("ic_downloading") cell.icon.setImage(gifLoading, for: .normal) cell.subtitle.text = Core.Lang.Get(key: "LBL_DOWNLOADING") // Download in background DispatchQueue.global(qos: .background).async { self.downloadResults(results: result, button: cell.icon, show: false) } } } return cell } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return (self.dbResults != nil ? self.dbResults!.count : 0) } func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? { return Core.Lang.Get(key: "BTN_DELETE") } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { if(self.dbResults != nil && self.dbResults!.count > 0 && indexPath.row >= 0 && indexPath.row < self.dbResults!.count && !self.CurrentDownloadProcessing) { let result = self.dbResults![indexPath.row] if(Core.Database.Results.Delete(results: result)) { self.loadResults(onlyLocale: true) self.deleteOnServer(results: result) } } } } //MARK: END TABLE FUNCTIONS //MARK: BEGIN Download & PDF FUNCTIONS @objc func onTableRowIconClick(sender: UIButton) { let button = sender as UIButton if(self.dbResults != nil && self.dbResults!.count > 0 && button.tag >= 0 && button.tag < self.dbResults!.count && !self.CurrentDownloadProcessing) { let results = self.dbResults![button.tag] if(results.GetStatus() == .COMPLETED && results.available && !results.picked_up && !self.CurrentDownloadProcessing) { let gifLoading = UIImage.gifImageWithName("ic_downloading") sender.setImage(gifLoading, for: .normal) self.downloadResults(results: results, button: button) } else if(results.GetStatus() == .COMPLETED && results.available && results.picked_up) { self.encryptPDF(results: results) } } } //MARK: send the download request to the server private func downloadResults(results: Core.Models.Database.Results, button: UIButton!, show: Bool = true) { self.CurrentDownloadProcessing = true let subscribe = Core.Models.Request.Subscribe() subscribe.udid = AppDelegate.Session.Device!.udid subscribe.device_token = AppDelegate.Session.Device!.token! subscribe.pgs = results.pgs subscribe.client_public_key = ViewController.Settings!.public_key subscribe.verificator_hash = ViewController.Settings!.verificator_hash self.MainLoadingShow() let requestKeyPair = Core.Security.Curve25519.GenerateKeyPair() let currentHost = ViewController.Settings!.labor?.host ?? .DEVELOPMENT Core.Https.Request.KeyExchangeAsync(host: currentHost, keyPair: requestKeyPair, onSuccess: { publicKey in let sharedKey = requestKeyPair.GetSharedKey(peerPublicKeyBase64: publicKey.key) let encryptedRequest = Core.Models.Request.EncryptedRequest(descriptor: "Subscribe", contentObject: subscribe, requestType: .REQUEST_SUBSCRIBE, key: sharedKey!) Core.Https.Request.EncryptedRequestAsync(host: currentHost, controller: "results", action: "download", request: encryptedRequest, serverPublicKey: publicKey, keyPair: requestKeyPair, onSuccess: { encryptedResponse in self.CurrentDownload = encryptedResponse.Decrypt(key: sharedKey!) as? Core.Models.Response.Download if(self.CurrentDownload != nil) { self.CurrentDownloadResults = results DispatchQueue.main.async { if(button != nil) { button.setImage(results.GetStatusIcon(), for: .normal) } self.processEncryption(show: show) self.MainLoadingHide() } } else { DispatchQueue.main.async { self.MainLoadingHide() } } self.CurrentDownloadProcessing = false }, onError: { error in self.CurrentDownloadProcessing = false Core.Log.Error(msg: error, namespace: "ViewController", method: "downloadResults") DispatchQueue.main.async { self.MainLoadingHide() } }) }, onError: { error in Core.Log.Error(msg: error, namespace: "ViewController", method: "downloadResults") self.CurrentDownloadProcessing = false DispatchQueue.main.async { self.MainLoadingHide() } }) } //MARK: User has to confirm always decryption of encrypted results by master password private func encryptPDF(results: Core.Models.Database.Results) { if(results.GetStatus() == .COMPLETED && results.available && results.picked_up) { self.CurrentDownload = nil self.CurrentDownloadResults = results //showPasswordConfirm() self.processEncryption() } } //MARK: send pickedup checksum to the file private func sendPickedUp(checksum: Core.Models.Request.CheckFileChecksum) { self.MainLoadingShow() let requestKeyPair = Core.Security.Curve25519.GenerateKeyPair() let currentHost = ViewController.Settings!.labor?.host ?? .DEVELOPMENT Core.Https.Request.KeyExchangeAsync(host: currentHost, keyPair: requestKeyPair, onSuccess: { publicKey in let sharedKey = requestKeyPair.GetSharedKey(peerPublicKeyBase64: publicKey.key) let encryptedRequest = Core.Models.Request.EncryptedRequest(descriptor: "CheckFileChecksum", contentObject: checksum, requestType: .REQUEST_CHECKSUM, key: sharedKey!) Core.Https.Request.EncryptedRequestAsync(host: currentHost, controller: "results", action: "pickedup", request: encryptedRequest, serverPublicKey: publicKey, keyPair: requestKeyPair, onError: { error in Core.Log.Error(msg: error, namespace: "ViewController", method: "sendPickedUp") }) DispatchQueue.main.async { self.MainLoadingHide() } }, onError: { error in Core.Log.Error(msg: error, namespace: "ViewController", method: "sendPickedUp") }) } //MARK: deletes results on the server private func deleteOnServer(results: Core.Models.Database.Results) { self.MainLoadingShow() let requestKeyPair = Core.Security.Curve25519.GenerateKeyPair() let currentHost = ViewController.Settings!.labor?.host ?? .DEVELOPMENT Core.Https.Request.KeyExchangeAsync(host: currentHost, keyPair: requestKeyPair, onSuccess: { publicKey in let subscribe = Core.Models.Request.Subscribe() subscribe.udid = AppDelegate.Session.Device!.udid subscribe.device_token = AppDelegate.Session.Device!.token! subscribe.pgs = results.pgs subscribe.client_public_key = ViewController.Settings!.public_key subscribe.verificator_hash = ViewController.Settings!.verificator_hash let sharedKey = requestKeyPair.GetSharedKey(peerPublicKeyBase64: publicKey.key) let encryptedRequest = Core.Models.Request.EncryptedRequest(descriptor: "Subscribe", contentObject: subscribe, requestType: .REQUEST_SUBSCRIBE, key: sharedKey!) Core.Https.Request.EncryptedRequestAsync(host: currentHost, controller: "results", action: "unsubscribe", request: encryptedRequest, serverPublicKey: publicKey, keyPair: requestKeyPair, onError: { error in Core.Log.Error(msg: error, namespace: "ViewController", method: "deleteOnServer") }) DispatchQueue.main.async { self.MainLoadingHide() } }, onError: { error in Core.Log.Error(msg: error, namespace: "ViewController", method: "deleteOnServer") }) } private func showPasswordConfirm() { self.PopupPasswordConfirm_txtPassword.text = "" self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_PASSWORD")) self.PopupPasswordConfirm.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) self.PopupPasswordConfirm_Overlay.alpha = 0.0 self.PopupPasswordConfirm_Overlay.isHidden = false UIView.animate(withDuration: 0.24) { self.PopupPasswordConfirm.transform = CGAffineTransform.identity self.PopupPasswordConfirm_Overlay.alpha = 70.0 } } private func hidePasswordConfirm() { self.view.endEditing(true) if(!self.PopupPasswordConfirm_Loading.isAnimating) { UIView.animate(withDuration: 0.24, animations: { self.PopupPasswordConfirm_Overlay.alpha = 0.0 self.PopupPasswordConfirm.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) }) {_ in self.PopupPasswordConfirm_Overlay.isHidden = true } } } //MARK: download encrypted results from the server and let them user decrypt it over password confirmation popup private func encryptDownload(show: Bool = true) { if(self.CurrentDownload != nil) { let decryptedPrivateKey = Core.Security.AES.Decrypt(value: ViewController.Settings!.hashed_private_key!, password: AppDelegate.Session.DevicePassword) if (decryptedPrivateKey != nil) { let keyPair = Core.Security.Curve25519.CreateKeyPair(_privateKey: decryptedPrivateKey!, _publicKey: ViewController.Settings!.public_key!) let sharedKey = keyPair.GetSharedKey(peerPublicKeyBase64: self.CurrentDownload!.server_public_key!) if(sharedKey != nil) { let decrypted_content = Core.Security.AES.Decrypt(value: self.CurrentDownload!.encrypted_content!, deriveKey: sharedKey!) if(decrypted_content != nil) { let local_encrypted = Core.Security.AES.Encrypt(value: decrypted_content!, password: AppDelegate.Session.DevicePassword) if(local_encrypted != nil) { let now = Date() let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" self.CurrentDownloadResults!.picked_up = true self.CurrentDownloadResults!.picked_up_ts = dateFormatter.string(from: now) self.CurrentDownloadResults!.file_checksum = Core.Security.SHA512.Encrypt(value: decrypted_content!) if(Core.System.WriteToEncryptedStorage(filename: self.CurrentDownloadResults!.pgs!, data: local_encrypted!.data(using: .utf8)!) && Core.Database.Results.Update(results: self.CurrentDownloadResults!)) { self.loadResults(onlyLocale: true) if(show) { self.loadPDF(decryptedContent: Data(base64Encoded: decrypted_content!)!) } let checksum = Core.Models.Request.CheckFileChecksum() checksum.pgs = self.CurrentDownloadResults!.pgs checksum.udid = AppDelegate.Session.Device!.udid checksum.device_token = AppDelegate.Session.Device!.token! checksum.client_public_key = ViewController.Settings!.public_key checksum.verificator_hash = ViewController.Settings!.verificator_hash checksum.file_checksum = self.CurrentDownloadResults!.file_checksum self.sendPickedUp(checksum: checksum) self.CurrentDownload = nil self.CurrentDownloadResults = nil } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_SAVE_ENCRYPTED")) Core.Log.Critical(msg: "could not store encrypted downloaded results", namespace: "ViewController", method: "encryptDownload") } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_SAVE_ENCRYPTED")) Core.Log.Critical(msg: "could not store encrypted downloaded results", namespace: "ViewController", method: "encryptDownload") } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_DECRYPT")) Core.Log.Critical(msg: "could not decrypt downloaded results", namespace: "ViewController", method: "encryptDownload") } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_DECRYPT")) Core.Log.Critical(msg: "could not decrypt downloaded results", namespace: "ViewController", method: "encryptDownload") } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_INVALID_PASSWORD")) } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_DOWNLOAD_NOT_FOUND")) Core.Log.Critical(msg: "no download found for encryption", namespace: "ViewController", method: "encryptDownload") } } /** * Loads encrypted file from the encrypted storage and decrypts it by master password of user */ private func encryptFile() { if(self.CurrentDownloadResults != nil) { let filePath = Core.System.GetURLForStorageEncryptedFile(filename: self.CurrentDownloadResults!.pgs!) if(filePath != nil && Core.System.FileExists(atPath: filePath!.path)) { let encrypted_data = Core.System.ReadFromEncryptedStorage(filename: self.CurrentDownloadResults!.pgs!) if(encrypted_data != nil) { let encrypted_content = String(data: encrypted_data!, encoding: .utf8)! let local_decrypted = Core.Security.AES.Decrypt(value: encrypted_content, password: AppDelegate.Session.DevicePassword) if(local_decrypted != nil) { self.loadResults() self.loadPDF(decryptedContent: Data(base64Encoded: local_decrypted!)!) self.CurrentDownload = nil self.CurrentDownloadResults = nil } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_INVALID_PASSWORD")) } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_ENCRYPTED_FILE_NOT_FOUND")) Core.Log.Critical(msg: "encrypted file not found", namespace: "ViewController", method: "encryptFile") } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_ENCRYPTED_FILE_NOT_FOUND")) Core.Log.Critical(msg: "encrypted file not found", namespace: "ViewController", method: "encryptFile") } } else { self.PopupPasswordConfirm_HideLoading(message: Core.Lang.Get(key: "ERROR_ENCRYPTED_FILE_NOT_FOUND")) Core.Log.Critical(msg: "encrypted file not found", namespace: "ViewController", method: "encryptFile") } } //MARK: Loads PDFViewer with decrypted content private func loadPDF(decryptedContent: Data) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let pdfViewController = storyboard.instantiateViewController(withIdentifier: "PDFViewController") as! PDFViewController pdfViewController.setContent(data: decryptedContent) pdfViewController.modalPresentationStyle = .fullScreen let transition = CATransition() transition.duration = 0.25 transition.type = .push transition.subtype = .fromRight self.view.window!.layer.add(transition, forKey: kCATransition) self.present(pdfViewController, animated: false) } @IBAction func PopupPasswordConfirm_BtnCancelClick(_ sender: Any) { self.hidePasswordConfirm() } @IBAction func PopupPasswordConfirm_BtnEncryptClick(_ sender: Any) { self.processEncryption() } /** * Encrypts ether already downloaded results from the device or downloads first the results from the server and encrypts thet after downloading */ private func processEncryption(show: Bool = true) { if(self.CurrentDownload != nil) { self.encryptDownload(show: show) } else if(self.CurrentDownloadResults != nil) { self.encryptFile() } } //MARK: END Download & PDF FUNCTIONS //MARK: BEGIN Bottom Menu @IBAction func BottonMenuOpenerClick(_ sender: Any) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let settingsPwdController = storyboard.instantiateViewController(identifier: "SettingsController") settingsPwdController.modalPresentationStyle = .fullScreen let transition = CATransition() transition.duration = 0.25 transition.type = .push transition.subtype = .fromRight self.view.window!.layer.add(transition, forKey: kCATransition) self.present(settingsPwdController, animated: false) } //MARK: END Bottom Menu }