From 2c2c9a06044ff39ca9e28a62ddead5158ae7fbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Mon, 7 Mar 2022 16:12:18 +0100 Subject: [PATCH 01/28] start deactivation screen & logic implementation --- DP3TApp.xcodeproj/project.pbxproj | 16 ++++ .../CheckIn/ProblematicEventsManager.swift | 4 +- .../Logic/Config/ConfigLoadOperation.swift | 9 ++ DP3TApp/Logic/Config/ConfigManager.swift | 18 ++++ DP3TApp/Logic/Config/ConfigResponseBody.swift | 3 + .../Deactivated/NSDeactivatedInfoView.swift | 67 ++++++++++++++ .../NSDeactivatedInfoViewController.swift | 88 +++++++++++++++++++ .../Base/NSSimpleModuleBaseView.swift | 20 ++++- 8 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift create mode 100644 DP3TApp/Screens/Deactivated/NSDeactivatedInfoViewController.swift diff --git a/DP3TApp.xcodeproj/project.pbxproj b/DP3TApp.xcodeproj/project.pbxproj index f45a33f20..6ff1f56a2 100644 --- a/DP3TApp.xcodeproj/project.pbxproj +++ b/DP3TApp.xcodeproj/project.pbxproj @@ -508,6 +508,8 @@ DCE1398E26382B5E0093D8F6 /* CreatedEventsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE1398C26382B5E0093D8F6 /* CreatedEventsManager.swift */; }; DCE32DF52637D05F00168693 /* QRCodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE32DF42637D05F00168693 /* QRCodeTests.swift */; }; DF3BA22324520EE3009086E7 /* NSDebugDatabaseUploadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3BA22224520EE3009086E7 /* NSDebugDatabaseUploadHelper.swift */; }; + F223579E27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F223579D27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift */; }; + F22357A027D60473001EFCD6 /* NSDeactivatedInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F223579F27D60473001EFCD6 /* NSDeactivatedInfoViewController.swift */; }; F806D33C24F91C7800672DFC /* LocalPushProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F806D33B24F91C7800672DFC /* LocalPushProtocol.swift */; }; F806D33D24F91C7800672DFC /* LocalPushProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F806D33B24F91C7800672DFC /* LocalPushProtocol.swift */; }; F806D33F24F91D5B00672DFC /* TracingManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F806D33E24F91D5B00672DFC /* TracingManagerTests.swift */; }; @@ -1007,6 +1009,8 @@ DCE1398C26382B5E0093D8F6 /* CreatedEventsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatedEventsManager.swift; sourceTree = ""; }; DCE32DF42637D05F00168693 /* QRCodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeTests.swift; sourceTree = ""; }; DF3BA22224520EE3009086E7 /* NSDebugDatabaseUploadHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSDebugDatabaseUploadHelper.swift; sourceTree = ""; }; + F223579D27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSDeactivatedInfoView.swift; sourceTree = ""; }; + F223579F27D60473001EFCD6 /* NSDeactivatedInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSDeactivatedInfoViewController.swift; sourceTree = ""; }; F806D33B24F91C7800672DFC /* LocalPushProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalPushProtocol.swift; sourceTree = ""; }; F806D33E24F91D5B00672DFC /* TracingManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingManagerTests.swift; sourceTree = ""; }; F806D34024F91DB900672DFC /* MockIdentifierProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIdentifierProvider.swift; sourceTree = ""; }; @@ -1927,6 +1931,7 @@ DC286A27242CE1D2001D5344 /* Screens */ = { isa = PBXGroup; children = ( + F223579C27D5F780001EFCD6 /* Deactivated */, 6EB9887727158EFD00DFFA17 /* Vaccination */, F87E96AE2639966400755AF1 /* Info */, F8D581242589701C00307C2B /* Unsupported */, @@ -2056,6 +2061,15 @@ path = Views; sourceTree = ""; }; + F223579C27D5F780001EFCD6 /* Deactivated */ = { + isa = PBXGroup; + children = ( + F223579D27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift */, + F223579F27D60473001EFCD6 /* NSDeactivatedInfoViewController.swift */, + ); + path = Deactivated; + sourceTree = ""; + }; F80465142525F58900311802 /* Tutorials */ = { isa = PBXGroup; children = ( @@ -3124,6 +3138,7 @@ 6E1771562440B5140008D73D /* NSCodeInputControl.swift in Sources */, F851223D250A2676009BE733 /* NSChartLineView.swift in Sources */, DC01360925DFE5D6001A33F4 /* StatisticInfoPopupType.swift in Sources */, + F22357A027D60473001EFCD6 /* NSDeactivatedInfoViewController.swift in Sources */, 242D21D1245C3853005DAEA8 /* Logger.swift in Sources */, 6EFB09792445B2CA0097BD3D /* NSInfoBoxView.swift in Sources */, 6EF4D24824582BBB005E2A9C /* NSExternalLinkButton.swift in Sources */, @@ -3167,6 +3182,7 @@ 6E3F65F72449B61A00980A4E /* NSDebugscreenViewController.swift in Sources */, F80E40632508F83100876906 /* NSStatisticsHeaderView.swift in Sources */, DC1617A32638717800B56ADA /* NSCheckInCurrentStateModuleView.swift in Sources */, + F223579E27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift in Sources */, DCA3FFBC2451621D0003F5AD /* NSSplashViewController.swift in Sources */, DC16178F2638717800B56ADA /* NSDiaryEntryContentView.swift in Sources */, F80E40662508FE1600876906 /* NSStatsticsModuleHeader.swift in Sources */, diff --git a/DP3TApp/Logic/CheckIn/ProblematicEventsManager.swift b/DP3TApp/Logic/CheckIn/ProblematicEventsManager.swift index d67d8d6e1..93b347768 100644 --- a/DP3TApp/Logic/CheckIn/ProblematicEventsManager.swift +++ b/DP3TApp/Logic/CheckIn/ProblematicEventsManager.swift @@ -65,7 +65,9 @@ class ProblematicEventsManager { syncNeeded = false } - if syncNeeded { + let deactivated = (ConfigManager.currentConfig?.deactivate ?? false) + + if syncNeeded, !deactivated { sync { _, _ in } } } diff --git a/DP3TApp/Logic/Config/ConfigLoadOperation.swift b/DP3TApp/Logic/Config/ConfigLoadOperation.swift index fb41b301a..17991a1d9 100644 --- a/DP3TApp/Logic/Config/ConfigLoadOperation.swift +++ b/DP3TApp/Logic/Config/ConfigLoadOperation.swift @@ -33,6 +33,15 @@ class ConfigLoadOperation: Operation { ConfigLoadOperation.presentedConfigForVersion = ConfigManager.appVersion } + } else if let c = config, c.deactivate { + let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) + vc.modalPresentationStyle = .fullScreen + + let appDelegate = UIApplication.shared.delegate as! AppDelegate + appDelegate.window?.rootViewController?.present(vc, animated: true, completion: nil) + + TracingManager.shared.endTracing() + CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) } else { self.cancel() } diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 174008fda..7e404dc10 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -31,6 +31,7 @@ class ConfigManager: NSObject { if let config = currentConfig?.iOSGaenSdkConfig { ConfigManager.updateSDKParameters(config: config) } + ConfigManager.presentDeactivationIfNeeded(config: currentConfig, window: nil) } } @@ -210,6 +211,23 @@ class ConfigManager: NSObject { } } } + + private static func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow?) { + if let config = config, config.deactivate { + let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) + vc.modalPresentationStyle = .fullScreen + + if let window = window { + window.rootViewController?.present(vc, animated: false, completion: nil) + } else { + let appDelegate = UIApplication.shared.delegate as! AppDelegate + appDelegate.window?.rootViewController?.present(vc, animated: true, completion: nil) + } + + TracingManager.shared.endTracing() + CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) + } + } } private extension UIViewController { diff --git a/DP3TApp/Logic/Config/ConfigResponseBody.swift b/DP3TApp/Logic/Config/ConfigResponseBody.swift index e9bc34d2f..2277afaf6 100644 --- a/DP3TApp/Logic/Config/ConfigResponseBody.swift +++ b/DP3TApp/Logic/Config/ConfigResponseBody.swift @@ -48,6 +48,9 @@ class ConfigResponseBody: UBCodable { public var showVaccinationInfo = false public let vaccinationBookingInfo: LocalizedValue + public let deactivate: Bool + public let deactivationMessage: LocalizedValue? + class InfoBox: UBCodable { let title, msg: String let url: URL? diff --git a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift new file mode 100644 index 000000000..c9f5aaa7e --- /dev/null +++ b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift @@ -0,0 +1,67 @@ +// +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 NSDeactivatedInfoView: NSSimpleModuleBaseView { + private let externalLinkButton: NSExternalLinkButton + private let externalLinkButtonWrapper = UIView() + + private let illuView = UIImageView(image: UIImage(named: "onboarding-prinzip")) + + init() { + externalLinkButton = NSExternalLinkButton(style: .normal(color: .ns_blue), size: .normal, linkType: .url) + + super.init(title: ConfigManager.currentConfig?.deactivationMessage?.value?.title ?? "", + subtitle: "accessibility_info_button".ub_localized, + subview: illuView, + text: ConfigManager.currentConfig?.deactivationMessage?.value?.msg ?? "", + subtitleColor: .ns_blue, + bottomPadding: true, + subviewOnTop: true) + + setup() + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setup() { + setupExternalLink() + } + + private func setupExternalLink() { + externalLinkButtonWrapper.addSubview(externalLinkButton) + contentView.addArrangedView(externalLinkButtonWrapper) + + externalLinkButtonWrapper.snp.makeConstraints { make in + make.left.right.equalToSuperview() + } + + externalLinkButton.title = ConfigManager.currentConfig?.deactivationMessage?.value?.urlTitle ?? "Weitere Informationen" + externalLinkButton.touchUpCallback = moreInfoTouched + + externalLinkButton.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview() + make.bottom.equalToSuperview().inset(NSPadding.large) + } + } + + private func moreInfoTouched() { + if let url = ConfigManager.currentConfig?.deactivationMessage?.value?.url { // URL(string: ConfigManager.currentConfig?.deactivationMessage?.value?.url ?? "https://www.google.ch") { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } else { + print("URL is null") + } + } +} diff --git a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoViewController.swift b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoViewController.swift new file mode 100644 index 000000000..47fcdc9a9 --- /dev/null +++ b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoViewController.swift @@ -0,0 +1,88 @@ +// +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 NSDeactivatedInfoViewController: NSViewController { + private let stackScrollView = NSStackScrollView(axis: .vertical, spacing: 0) + + private let deactivatedInfoView = NSDeactivatedInfoView() + + override init() { + super.init() + + title = "app_name".ub_localized + } + + // MARK: - View + + override func viewDidLoad() { + super.viewDidLoad() + + stackScrollView.stackView.isLayoutMarginsRelativeArrangement = true + stackScrollView.stackView.layoutMargins = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) + + view.addSubview(stackScrollView) + stackScrollView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + view.backgroundColor = .setColorsForTheme(lightColor: .ns_backgroundSecondary, darkColor: .ns_background) + + setupLayout() + + // Ensure that Screen builds without animation if app not started on homescreen + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.finishTransition?() + self.finishTransition = nil + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + finishTransition?() + finishTransition = nil + } + + private var finishTransition: (() -> Void)? + + private func setupLayout() { + // navigation bar + let image = UIImage(named: "ic-info-outline") + navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, landscapeImagePhone: image, style: .plain, target: self, action: #selector(infoButtonPressed)) + navigationItem.rightBarButtonItem?.tintColor = .ns_blue + navigationItem.rightBarButtonItem?.accessibilityLabel = "accessibility_info_button_app".ub_localized + + let swissFlagImage = UIImage(named: "ic_navbar_schweiz_wappen")?.withRenderingMode(.alwaysOriginal) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIImageView(image: swissFlagImage)) + + stackScrollView.addSpacerView(NSPadding.large) + + stackScrollView.addArrangedView(deactivatedInfoView) + + deactivatedInfoView.alpha = 0 + + finishTransition = { + UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: [.allowUserInteraction], animations: { + self.view.layoutIfNeeded() + }, completion: nil) + + UIView.animate(withDuration: 0.3, delay: 0.35, options: [.allowUserInteraction], animations: { + self.deactivatedInfoView.alpha = 1 + }, completion: nil) + } + } + + @objc private func infoButtonPressed() { + present(NSNavigationController(rootViewController: NSAboutViewController()), animated: true) + } +} diff --git a/DP3TApp/Screens/Homescreen/Homescreen/Base/NSSimpleModuleBaseView.swift b/DP3TApp/Screens/Homescreen/Homescreen/Base/NSSimpleModuleBaseView.swift index 59c2c829b..78541023e 100644 --- a/DP3TApp/Screens/Homescreen/Homescreen/Base/NSSimpleModuleBaseView.swift +++ b/DP3TApp/Screens/Homescreen/Homescreen/Base/NSSimpleModuleBaseView.swift @@ -27,6 +27,7 @@ class NSSimpleModuleBaseView: UIView { private let sideInset: CGFloat private let bottomPadding: Bool + private let subviewOnTop: Bool // MARK: - Public @@ -34,10 +35,11 @@ class NSSimpleModuleBaseView: UIView { // MARK: - Init - init(title: String, subtitle: String? = nil, subview: UIView? = nil, boldText: String? = nil, text: String? = nil, image: UIImage? = nil, subtitleColor: UIColor? = nil, bottomPadding: Bool = true) { + init(title: String, subtitle: String? = nil, subview: UIView? = nil, boldText: String? = nil, text: String? = nil, image: UIImage? = nil, subtitleColor: UIColor? = nil, bottomPadding: Bool = true, subviewOnTop: Bool = false) { sideInset = NSPadding.large self.bottomPadding = bottomPadding self.subview = subview + self.subviewOnTop = subviewOnTop super.init(frame: .zero) translatesAutoresizingMaskIntoConstraints = false @@ -67,6 +69,14 @@ class NSSimpleModuleBaseView: UIView { ub_addShadow(radius: 4, opacity: 0.1, xOffset: 0, yOffset: -1) let topInset = NSPadding.medium + NSPadding.small + if let sv = subview, subviewOnTop { + addSubview(sv) + sv.snp.makeConstraints { make in + make.top.equalToSuperview().inset(topInset) + make.centerX.equalToSuperview() + } + } + let titleContentStackView = UIStackView() titleContentStackView.axis = .vertical titleContentStackView.alignment = .leading @@ -77,7 +87,11 @@ class NSSimpleModuleBaseView: UIView { addSubview(titleContentStackView) titleContentStackView.snp.makeConstraints { make in - make.top.equalToSuperview().inset(topInset) + if let sv = subview, subviewOnTop { + make.top.equalTo(sv.snp.bottom).offset(NSPadding.large) + } else { + make.top.equalToSuperview().inset(topInset) + } make.left.right.equalToSuperview().inset(sideInset) } addSubview(contentView) @@ -117,7 +131,7 @@ class NSSimpleModuleBaseView: UIView { make.bottom.lessThanOrEqualToSuperview() } - if let subview = subview { + if let subview = subview, !subviewOnTop { view.addSubview(subview) subview.snp.makeConstraints { make in make.top.equalTo(textLabel.snp.bottom).offset(NSPadding.large) From 69e5b8f3faab9fba68527ff7b98b9cef3de6c12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Mon, 7 Mar 2022 17:24:59 +0100 Subject: [PATCH 02/28] continue logic implementation --- DP3TApp/Logic/AppDelegate.swift | 2 ++ DP3TApp/Logic/Config/ConfigManager.swift | 11 +++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/DP3TApp/Logic/AppDelegate.swift b/DP3TApp/Logic/AppDelegate.swift index 044c42b1f..9ecb44661 100644 --- a/DP3TApp/Logic/AppDelegate.swift +++ b/DP3TApp/Logic/AppDelegate.swift @@ -114,6 +114,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.makeKeyAndVisible() + ConfigManager().startConfigRequest(window: window) + if UserStorage.shared.appClipCheckinUrl() != nil { let checkinOnboardingVC = NSCheckinOnboardingViewController() checkinOnboardingVC.modalPresentationStyle = .fullScreen diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 7e404dc10..23b314b45 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -31,7 +31,6 @@ class ConfigManager: NSObject { if let config = currentConfig?.iOSGaenSdkConfig { ConfigManager.updateSDKParameters(config: config) } - ConfigManager.presentDeactivationIfNeeded(config: currentConfig, window: nil) } } @@ -45,7 +44,7 @@ class ConfigManager: NSObject { static let configBackgroundValidityInterval: TimeInterval = 60 * 60 * 6 // 6h static var allowTracing: Bool { - return true + return !(ConfigManager.currentConfig?.deactivate ?? false) } // MARK: - Version Numbers @@ -162,6 +161,7 @@ class ConfigManager: NSObject { // self must be strong if let config = config { self.presentAlertIfNeeded(config: config, window: window) + self.presentDeactivationIfNeeded(config: config, window: window) } } } @@ -212,16 +212,15 @@ class ConfigManager: NSObject { } } - private static func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow?) { + private func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow?) { if let config = config, config.deactivate { let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) - vc.modalPresentationStyle = .fullScreen if let window = window { - window.rootViewController?.present(vc, animated: false, completion: nil) + window.rootViewController = vc } else { let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.window?.rootViewController?.present(vc, animated: true, completion: nil) + appDelegate.window?.rootViewController? = vc } TracingManager.shared.endTracing() From fc1287b2186e9977665d47919bbd0b8e83252b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Wed, 9 Mar 2022 17:25:17 +0100 Subject: [PATCH 03/28] assures background tasks not scheduled if app deactivated (needs SDK update to build :) --- DP3TApp/Logic/AppDelegate.swift | 4 +++ DP3TApp/Logic/Config/ConfigManager.swift | 36 +++++++++++++++++----- DP3TApp/Logic/Tracing/TracingManager.swift | 8 ++++- DP3TApp/Logic/User/UserStorage.swift | 3 ++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/DP3TApp/Logic/AppDelegate.swift b/DP3TApp/Logic/AppDelegate.swift index 9ecb44661..0eacbd72f 100644 --- a/DP3TApp/Logic/AppDelegate.swift +++ b/DP3TApp/Logic/AppDelegate.swift @@ -116,6 +116,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ConfigManager().startConfigRequest(window: window) + let deactivated = UserStorage.shared.appDeactivated + + guard !deactivated else { return } + if UserStorage.shared.appClipCheckinUrl() != nil { let checkinOnboardingVC = NSCheckinOnboardingViewController() checkinOnboardingVC.modalPresentationStyle = .fullScreen diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 23b314b45..6ed845e1a 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -160,8 +160,8 @@ class ConfigManager: NSObject { loadConfig(backgroundTask: false) { config in // self must be strong if let config = config { - self.presentAlertIfNeeded(config: config, window: window) self.presentDeactivationIfNeeded(config: config, window: window) + self.presentAlertIfNeeded(config: config, window: window) } } } @@ -212,19 +212,41 @@ class ConfigManager: NSObject { } } - private func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow?) { - if let config = config, config.deactivate { + public func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow?) { + guard let config = config else { return } + + if config.deactivate, !UserStorage.shared.appDeactivated { + if TracingManager.shared.isActivated { + TracingManager.shared.endTracing() + } + + TracingManager.shared.endTracing() + TracingManager.shared.setBackgroundRefreshEnabled(false) + UBPushManager.shared.setActive(false) + CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) + UserStorage.shared.appDeactivated = true + + } else if !config.deactivate && UserStorage.shared.appDeactivated { + TracingManager.shared.startTracing() + TracingManager.shared.setBackgroundRefreshEnabled(true) + UserStorage.shared.appDeactivated = false + + if !UserStorage.shared.hasCompletedOnboarding { + let onboardingViewController = NSOnboardingViewController() + onboardingViewController.modalPresentationStyle = .fullScreen + window?.rootViewController?.present(onboardingViewController, animated: false) + } + } + + if config.deactivate { let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) if let window = window { window.rootViewController = vc } else { - let appDelegate = UIApplication.shared.delegate as! AppDelegate + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } appDelegate.window?.rootViewController? = vc } - - TracingManager.shared.endTracing() - CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) } } } diff --git a/DP3TApp/Logic/Tracing/TracingManager.swift b/DP3TApp/Logic/Tracing/TracingManager.swift index 4d0c37b7f..0f70423d6 100644 --- a/DP3TApp/Logic/Tracing/TracingManager.swift +++ b/DP3TApp/Logic/Tracing/TracingManager.swift @@ -250,6 +250,12 @@ class TracingManager: NSObject { completion?() } } + + func setBackgroundRefreshEnabled(_ enabled: Bool) { + guard #available(iOS 12.5, *) else { return } + + DP3TTracing.setBackgroundTasksEnabled(enabled) + } } extension TracingManager: DP3TTracingDelegate { @@ -271,7 +277,7 @@ extension TracingManager: DP3TTracingDelegate { isAuthorized = (state.trackingState != .inactive(error: .authorizationUnknown) && state.trackingState != .inactive(error: .permissionError)) - let needsPush = !isAuthorized && CrowdNotifier.hasCheckins() + let needsPush = !isAuthorized && CrowdNotifier.hasCheckins() && !UserStorage.shared.appDeactivated UBPushManager.shared.setActive(needsPush) // update tracing error states if needed diff --git a/DP3TApp/Logic/User/UserStorage.swift b/DP3TApp/Logic/User/UserStorage.swift index 1ec81bbe3..8f75cbaa2 100644 --- a/DP3TApp/Logic/User/UserStorage.swift +++ b/DP3TApp/Logic/User/UserStorage.swift @@ -77,6 +77,9 @@ class UserStorage { @UBOptionalUserDefault(key: "lastTracingDisabledDate") var lastTracingDisabledDate: Date? + @UBUserDefault(key: "appDeactivated", defaultValue: false) + var appDeactivated: Bool + // method to get AppClip url in Main App public func appClipCheckinUrl() -> String? { let bi = (Bundle.main.bundleIdentifier ?? "") From 928528d64fbc105ecca1e558fa8840efc1112101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Fri, 11 Mar 2022 10:51:35 +0100 Subject: [PATCH 04/28] update SDK, add correct illu, only endTracing if it's currently active --- DP3TApp.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- DP3TApp/Logic/Config/ConfigManager.swift | 28 ++++----- .../illu-shutdown.imageset/Contents.json | 56 ++++++++++++++++++ .../shutdown-illu-dark.png | Bin 0 -> 4685 bytes .../shutdown-illu-dark@2x.png | Bin 0 -> 8484 bytes .../shutdown-illu-dark@3x.png | Bin 0 -> 12566 bytes .../shutdown-illu-light.png | Bin 0 -> 4770 bytes .../shutdown-illu-light@2x.png | Bin 0 -> 8694 bytes .../shutdown-illu-light@3x.png | Bin 0 -> 12649 bytes .../Deactivated/NSDeactivatedInfoView.swift | 2 +- 11 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/Contents.json create mode 100644 DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-dark.png create mode 100644 DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-dark@2x.png create mode 100644 DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-dark@3x.png create mode 100644 DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-light.png create mode 100644 DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-light@2x.png create mode 100644 DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-light@3x.png diff --git a/DP3TApp.xcodeproj/project.pbxproj b/DP3TApp.xcodeproj/project.pbxproj index 6ff1f56a2..208c5dbed 100644 --- a/DP3TApp.xcodeproj/project.pbxproj +++ b/DP3TApp.xcodeproj/project.pbxproj @@ -4727,7 +4727,7 @@ repositoryURL = "https://github.com/DP-3T/dp3t-sdk-ios.git"; requirement = { kind = exactVersion; - version = 2.4.0; + version = 2.5.0; }; }; F870A5B52492C6D500C34FFA /* XCRemoteSwiftPackageReference "SQLite" */ = { diff --git a/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7724c0bf4..485fc949c 100644 --- a/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/DP-3T/dp3t-sdk-ios.git", "state": { "branch": null, - "revision": "f0897a3e013e8ad3e693288f82757068b4ce08f2", - "version": "2.4.0" + "revision": "69d3e7863a7d8f9c230745b705481f2ade461e10", + "version": "2.5.0" } }, { diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 6ed845e1a..206bb9467 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -215,17 +215,26 @@ class ConfigManager: NSObject { public func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow?) { guard let config = config else { return } - if config.deactivate, !UserStorage.shared.appDeactivated { + if config.deactivate { + let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) + + if let window = window { + window.rootViewController = vc + } else { + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + appDelegate.window?.rootViewController? = vc + } + if TracingManager.shared.isActivated { TracingManager.shared.endTracing() } - TracingManager.shared.endTracing() TracingManager.shared.setBackgroundRefreshEnabled(false) UBPushManager.shared.setActive(false) CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) - UserStorage.shared.appDeactivated = true - + if !UserStorage.shared.appDeactivated { + UserStorage.shared.appDeactivated = true + } } else if !config.deactivate && UserStorage.shared.appDeactivated { TracingManager.shared.startTracing() TracingManager.shared.setBackgroundRefreshEnabled(true) @@ -237,17 +246,6 @@ class ConfigManager: NSObject { window?.rootViewController?.present(onboardingViewController, animated: false) } } - - if config.deactivate { - let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) - - if let window = window { - window.rootViewController = vc - } else { - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - appDelegate.window?.rootViewController? = vc - } - } } } diff --git a/DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/Contents.json b/DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/Contents.json new file mode 100644 index 000000000..a83fd430e --- /dev/null +++ b/DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/Contents.json @@ -0,0 +1,56 @@ +{ + "images" : [ + { + "filename" : "shutdown-illu-light.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "shutdown-illu-dark.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "shutdown-illu-light@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "shutdown-illu-dark@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "shutdown-illu-light@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "shutdown-illu-dark@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-dark.png b/DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..9575d034dce22c91b4fdf9999d9f2a4b751d429d GIT binary patch literal 4685 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 z�=bk)C>&aTe%l+A`p!h}WTu>26)JiuaJ;$@VTKAbohV1v1VIoueI~8v~b<(bp;O zA-~j{B{_CPMjvT~j59$8^8@t?lD2HDCjj^>USLS&w$ep;*gaG??07ZKH>{o)6{2sQd5JjUF>^!^n@8YYUgvr z4jO`-*!d`L$Mx3x?-l-Q*n^DIqk}Z3jrcuWnnR-31ne^nx`?mmx3j~s5_y8uv6t_3 zheG$`@jeeSPLECs;{r*uYaXWP!tQpqdAwdlBrdJ{t(Uvx^Ej(r^AG;KDMJyh z87J9D1(Gz{)tG5liK~o6~on-uEEmWb~?bFc1p)Ca!!rMxiHU zuWwrLgT-$Z^5UMZ1V@5@#f`(w=?*x}b*lFEntgllcje`>j{URYH|hf9m zekE!6<3Ij*C!$|9F{{TCLR|N~x1DH%H#R4l?}1tu_0U>&J$mwm^bZF&uIt}s=#{*+ z{Q`1L*UY=kcP0nLgw-?NcUQKAT%w2GNbGqedU7g^N57-&>XPW}8u-D>V+{hg`;0yU zp+}~qNkH+^$5-72Mx}rn+_<81Vpt(xzon$%u!qO`5up2g`EBGD^p^!?{2RbS=wR;u z1%X8m{h*@%qH|8&PSan~Ibh;Fk2jDDezu41CFH{ZeaKgGLWdq&m{QT9kKqNJl7HUl!)7@c z-Al-WLhZV*=utg75m}aT*A?(7?J$=CJ^Bib?jhvC?smmj^o|Xq7+zS`qEEfh?ZO81 zo9k%wP@n5e1$MW-b4TaJ&5PxJbo zM3G`hToRUOSjz##G>||VD?Q|}9#*s>?XpVSVH1amNGR8xDsk*MbpLuz;y{wduA$9l zn0rALLVcPS^Z7GL)Ji%jw)DyC@rQSls_!wuG9C7rbnnp6s^2azy?;Qb2Ny7k;-OQV zfY1*n{qV?tKRc^;g6Gl4K(GRLICOYuYQNS@=N|R3=*M!OLq9O`hWF*-0@ZmrI_Rx> zPo?Jz_zJpW9}syb?b6g*$vy*J@l*3h27RwtUN>n2YDYH*^<2LMap*4vXINUwby@Vi zBHtbinoajM9qlF=bzNeaM_-B2L6&qDec#BxF*Py}`O)>agW68$*LZZX8k&;(8k4?X zVFS%vugR=$&91G+?3IleotAPAhrZt||937k3ORIRCG-bpOBaOMbj6nNK42|29f;Aj ztQ*Bx( z^gwocONf3GMcea|+EH{7f+S=Na3YN0DQ4A^bR2HONtS|5Li+91HyqGRAYNup4d$ P00000NkvXXu0mjfgMeni literal 0 HcmV?d00001 diff --git a/DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-dark@2x.png b/DP3TApp/Resources/Assets.xcassets/Illustrations/illu-shutdown.imageset/shutdown-illu-dark@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e2c31149685f8a4fb78e669acacf10d282e198c5 GIT binary patch literal 8484 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 JIqQdt=x8E0fOWE>CzS8(xuBw6lA24 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 literal 0 HcmV?d00001 diff --git a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift index c9f5aaa7e..23238a33c 100644 --- a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift +++ b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift @@ -15,7 +15,7 @@ class NSDeactivatedInfoView: NSSimpleModuleBaseView { private let externalLinkButton: NSExternalLinkButton private let externalLinkButtonWrapper = UIView() - private let illuView = UIImageView(image: UIImage(named: "onboarding-prinzip")) + private let illuView = UIImageView(image: UIImage(named: "illu-shutdown")) init() { externalLinkButton = NSExternalLinkButton(style: .normal(color: .ns_blue), size: .normal, linkType: .url) From 5f5023a1204aff06a2a0a766217beb7d174d08e4 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 11 Mar 2022 14:58:48 +0100 Subject: [PATCH 05/28] Switch certificate pinning to Quovadis Root CA 2 G3 --- DP3TApp.xcodeproj/project.pbxproj | 78 ++---------------- .../Networking/Base/URLSession+pinning.swift | 19 +++-- .../Certificates/QuoVadis-Root-CA-2-G3.cer | Bin 0 -> 1380 bytes .../codegen-service-a.bag.admin.ch.der | Bin 1990 -> 0 bytes .../codegen-service-d.bag.admin.ch.der | Bin 1987 -> 0 bytes .../codegen-service-t.bag.admin.ch.der | Bin 1987 -> 0 bytes .../codegen-service.bag.admin.ch.der | Bin 1981 -> 0 bytes .../Certificates/www.pt-a.bfs.admin.ch.der | Bin 1936 -> 0 bytes .../Certificates/www.pt-d.bfs.admin.ch.der | Bin 1937 -> 0 bytes .../Certificates/www.pt-t.bfs.admin.ch.der | Bin 1937 -> 0 bytes .../Certificates/www.pt.bfs.admin.ch.der | Bin 1934 -> 0 bytes .../Certificates/www.pt1-a.bfs.admin.ch.der | Bin 1973 -> 0 bytes .../Certificates/www.pt1-d.bfs.admin.ch.der | Bin 1972 -> 0 bytes .../Certificates/www.pt1-t.bfs.admin.ch.der | Bin 1971 -> 0 bytes .../Certificates/www.pt1.bfs.admin.ch.der | Bin 1968 -> 0 bytes 15 files changed, 16 insertions(+), 81 deletions(-) create mode 100644 DP3TApp/Resources/Certificates/QuoVadis-Root-CA-2-G3.cer delete mode 100644 DP3TApp/Resources/Certificates/codegen-service-a.bag.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/codegen-service-d.bag.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/codegen-service-t.bag.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/codegen-service.bag.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt-a.bfs.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt-d.bfs.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt-t.bfs.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt.bfs.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt1-a.bfs.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt1-d.bfs.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt1-t.bfs.admin.ch.der delete mode 100644 DP3TApp/Resources/Certificates/www.pt1.bfs.admin.ch.der diff --git a/DP3TApp.xcodeproj/project.pbxproj b/DP3TApp.xcodeproj/project.pbxproj index f45a33f20..1f1e099d0 100644 --- a/DP3TApp.xcodeproj/project.pbxproj +++ b/DP3TApp.xcodeproj/project.pbxproj @@ -137,12 +137,6 @@ 242D2251245C4BD8005DAEA8 /* CodeValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2462BA122451F0F60046906D /* CodeValidator.swift */; }; 242D2252245C4BD8005DAEA8 /* UIStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D5EF692448CAE800C197D6 /* UIStateModel.swift */; }; 242D2254245C4BD8005DAEA8 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 242D21D3245C4BD8005DAEA8 /* SnapKit */; }; - 242D2257245C4BD8005DAEA8 /* codegen-service-a.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E7377245AFD2000C934B8 /* codegen-service-a.bag.admin.ch.der */; }; - 242D2258245C4BD8005DAEA8 /* codegen-service-d.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E7379245AFD2000C934B8 /* codegen-service-d.bag.admin.ch.der */; }; - 242D2259245C4BD8005DAEA8 /* codegen-service.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E7378245AFD2000C934B8 /* codegen-service.bag.admin.ch.der */; }; - 242D225A245C4BD8005DAEA8 /* www.pt1-a.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E736E245AF38E00C934B8 /* www.pt1-a.bfs.admin.ch.der */; }; - 242D225B245C4BD8005DAEA8 /* www.pt1-d.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E736F245AF38E00C934B8 /* www.pt1-d.bfs.admin.ch.der */; }; - 242D225C245C4BD8005DAEA8 /* www.pt1.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E736D245AF38E00C934B8 /* www.pt1.bfs.admin.ch.der */; }; 242D225E245C4BD8005DAEA8 /* Inter-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6E7C0D27242D04280017C4F9 /* Inter-SemiBold.ttf */; }; 242D225F245C4BD8005DAEA8 /* Inter-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6E7C0D28242D04280017C4F9 /* Inter-Bold.ttf */; }; 242D2260245C4BD8005DAEA8 /* Inter-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6E7C0D2A242D04280017C4F9 /* Inter-Light.ttf */; }; @@ -198,10 +192,6 @@ 3DCD3E152655585D0050945D /* NSCheckInDetailCheckInEndedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD3E142655585D0050945D /* NSCheckInDetailCheckInEndedView.swift */; }; 3DCD3E162655585D0050945D /* NSCheckInDetailCheckInEndedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD3E142655585D0050945D /* NSCheckInDetailCheckInEndedView.swift */; }; 3DCE4F4D264C1F8C000115C8 /* NSCovidCodeModuleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCE4F4C264C1F8C000115C8 /* NSCovidCodeModuleView.swift */; }; - 48E9679A2473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = 48E967992473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der */; }; - 48E9679B2473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = 48E967992473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der */; }; - 48E9679D2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = 48E9679C2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der */; }; - 48E9679E2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = 48E9679C2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der */; }; 6E0D1227263A92050014CF7C /* NSRoundImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E0D1226263A92050014CF7C /* NSRoundImageButton.swift */; }; 6E0D1228263A92050014CF7C /* NSRoundImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E0D1226263A92050014CF7C /* NSRoundImageButton.swift */; }; 6E0D1232263AA26C0014CF7C /* NSEventPDFViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E0D1231263AA26C0014CF7C /* NSEventPDFViewController.swift */; }; @@ -372,6 +362,8 @@ AAF73666242F2EA00051E34A /* NSEncountersModuleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAF73665242F2EA00051E34A /* NSEncountersModuleView.swift */; }; AAF73668242F3D030051E34A /* UIStackView+NS.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAF73667242F3D030051E34A /* UIStackView+NS.swift */; }; AAF7366B2430838A0051E34A /* NSAnimatedGraphLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAF7366A2430838A0051E34A /* NSAnimatedGraphLayer.swift */; }; + AC35602927DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer in Resources */ = {isa = PBXBuildFile; fileRef = AC35602827DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer */; }; + AC35602A27DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer in Resources */ = {isa = PBXBuildFile; fileRef = AC35602827DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer */; }; B67A11AE24B8CB1700B46306 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6AEA30724B74ACA0055B5BB /* NSImageView.swift */; }; B6AEA30824B74ACA0055B5BB /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6AEA30724B74ACA0055B5BB /* NSImageView.swift */; }; DC003DD326413E3A0027F379 /* UserUploadPayload.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC003DD226413E390027F379 /* UserUploadPayload.pb.swift */; }; @@ -538,13 +530,7 @@ F80E407A25092B0500876906 /* NSStatisticsChartContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80E407925092B0500876906 /* NSStatisticsChartContentView.swift */; }; F80E407B25092B0500876906 /* NSStatisticsChartContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80E407925092B0500876906 /* NSStatisticsChartContentView.swift */; }; F80E735A245AD7B400C934B8 /* URLSession+pinning.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80E7359245AD7B400C934B8 /* URLSession+pinning.swift */; }; - F80E7371245AF51C00C934B8 /* www.pt1-a.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E736E245AF38E00C934B8 /* www.pt1-a.bfs.admin.ch.der */; }; - F80E7372245AF51C00C934B8 /* www.pt1-d.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E736F245AF38E00C934B8 /* www.pt1-d.bfs.admin.ch.der */; }; - F80E7373245AF51C00C934B8 /* www.pt1.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E736D245AF38E00C934B8 /* www.pt1.bfs.admin.ch.der */; }; F80E7376245AF74C00C934B8 /* UBPinnedCertificatesTrustEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80E7375245AF74C00C934B8 /* UBPinnedCertificatesTrustEvaluator.swift */; }; - F80E737A245AFE4500C934B8 /* codegen-service-a.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E7377245AFD2000C934B8 /* codegen-service-a.bag.admin.ch.der */; }; - F80E737B245AFE4500C934B8 /* codegen-service-d.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E7379245AFD2000C934B8 /* codegen-service-d.bag.admin.ch.der */; }; - F80E737C245AFE4500C934B8 /* codegen-service.bag.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F80E7378245AFD2000C934B8 /* codegen-service.bag.admin.ch.der */; }; F82341D6258A41CF007A51BA /* NSUnsupportedOSNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F82341D5258A41CF007A51BA /* NSUnsupportedOSNotificationManager.swift */; }; F82341D7258A41CF007A51BA /* NSUnsupportedOSNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F82341D5258A41CF007A51BA /* NSUnsupportedOSNotificationManager.swift */; }; F8287D17246ABCAF0022CFD9 /* DP3TSDK in Frameworks */ = {isa = PBXBuildFile; productRef = F8287D16246ABCAF0022CFD9 /* DP3TSDK */; }; @@ -611,14 +597,6 @@ F86A6A9926383803003CAC1B /* NSVenueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86A6A9726383803003CAC1B /* NSVenueView.swift */; }; F870A5B72492C6D500C34FFA /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = F870A5B62492C6D500C34FFA /* SQLite */; }; F870A5B92492C6DE00C34FFA /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = F870A5B82492C6DE00C34FFA /* SQLite */; }; - F87685B525B5F89B00538B8B /* www.pt-a.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B125B5F89900538B8B /* www.pt-a.bfs.admin.ch.der */; }; - F87685B625B5F89B00538B8B /* www.pt-a.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B125B5F89900538B8B /* www.pt-a.bfs.admin.ch.der */; }; - F87685B725B5F89B00538B8B /* www.pt-d.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B225B5F89900538B8B /* www.pt-d.bfs.admin.ch.der */; }; - F87685B825B5F89B00538B8B /* www.pt-d.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B225B5F89900538B8B /* www.pt-d.bfs.admin.ch.der */; }; - F87685B925B5F89B00538B8B /* www.pt-t.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B325B5F89A00538B8B /* www.pt-t.bfs.admin.ch.der */; }; - F87685BA25B5F89B00538B8B /* www.pt-t.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B325B5F89A00538B8B /* www.pt-t.bfs.admin.ch.der */; }; - F87685BB25B5F89B00538B8B /* www.pt.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B425B5F89A00538B8B /* www.pt.bfs.admin.ch.der */; }; - F87685BC25B5F89B00538B8B /* www.pt.bfs.admin.ch.der in Resources */ = {isa = PBXBuildFile; fileRef = F87685B425B5F89A00538B8B /* www.pt.bfs.admin.ch.der */; }; F87C36A0258B6652008DCC81 /* ExposureNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F87C369F258B6652008DCC81 /* ExposureNotification.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; F87C36A1258B6868008DCC81 /* ExposureNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F87C369F258B6652008DCC81 /* ExposureNotification.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; F87C3710258C2613008DCC81 /* NSRadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F87C370D258C2613008DCC81 /* NSRadioButton.swift */; }; @@ -796,8 +774,6 @@ 3DCD3E0F2654256A0050945D /* NSReportsDetailExposedCheckInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSReportsDetailExposedCheckInViewController.swift; sourceTree = ""; }; 3DCD3E142655585D0050945D /* NSCheckInDetailCheckInEndedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSCheckInDetailCheckInEndedView.swift; sourceTree = ""; }; 3DCE4F4C264C1F8C000115C8 /* NSCovidCodeModuleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSCovidCodeModuleView.swift; sourceTree = ""; }; - 48E967992473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "codegen-service-t.bag.admin.ch.der"; sourceTree = ""; }; - 48E9679C2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "www.pt1-t.bfs.admin.ch.der"; sourceTree = ""; }; 6E0D1226263A92050014CF7C /* NSRoundImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSRoundImageButton.swift; sourceTree = ""; }; 6E0D1231263AA26C0014CF7C /* NSEventPDFViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NSEventPDFViewController.swift; path = DP3TApp/Screens/Checkin/Events/NSEventPDFViewController.swift; sourceTree = SOURCE_ROOT; }; 6E1771532440B09A0008D73D /* NSCodeInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSCodeInputViewController.swift; sourceTree = ""; }; @@ -920,6 +896,7 @@ AAF73665242F2EA00051E34A /* NSEncountersModuleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEncountersModuleView.swift; sourceTree = ""; }; AAF73667242F3D030051E34A /* UIStackView+NS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+NS.swift"; sourceTree = ""; }; AAF7366A2430838A0051E34A /* NSAnimatedGraphLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAnimatedGraphLayer.swift; sourceTree = ""; }; + AC35602827DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = "QuoVadis-Root-CA-2-G3.cer"; sourceTree = ""; }; B6AEA30724B74ACA0055B5BB /* NSImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; DC003DD126413DE30027F379 /* UserUploadPayload.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = UserUploadPayload.proto; sourceTree = ""; }; DC003DD226413E390027F379 /* UserUploadPayload.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserUploadPayload.pb.swift; sourceTree = ""; }; @@ -1024,13 +1001,7 @@ F80E407625092AE100876906 /* NSChartColumnView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSChartColumnView.swift; sourceTree = ""; }; F80E407925092B0500876906 /* NSStatisticsChartContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSStatisticsChartContentView.swift; sourceTree = ""; }; F80E7359245AD7B400C934B8 /* URLSession+pinning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+pinning.swift"; sourceTree = ""; }; - F80E736D245AF38E00C934B8 /* www.pt1.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = www.pt1.bfs.admin.ch.der; sourceTree = ""; }; - F80E736E245AF38E00C934B8 /* www.pt1-a.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "www.pt1-a.bfs.admin.ch.der"; sourceTree = ""; }; - F80E736F245AF38E00C934B8 /* www.pt1-d.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "www.pt1-d.bfs.admin.ch.der"; sourceTree = ""; }; F80E7375245AF74C00C934B8 /* UBPinnedCertificatesTrustEvaluator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UBPinnedCertificatesTrustEvaluator.swift; sourceTree = ""; }; - F80E7377245AFD2000C934B8 /* codegen-service-a.bag.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "codegen-service-a.bag.admin.ch.der"; sourceTree = ""; }; - F80E7378245AFD2000C934B8 /* codegen-service.bag.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "codegen-service.bag.admin.ch.der"; sourceTree = ""; }; - F80E7379245AFD2000C934B8 /* codegen-service-d.bag.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "codegen-service-d.bag.admin.ch.der"; sourceTree = ""; }; F82341D5258A41CF007A51BA /* NSUnsupportedOSNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSUnsupportedOSNotificationManager.swift; sourceTree = ""; }; F830798E24928EC4005D3C65 /* LoggingStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingStorage.swift; sourceTree = ""; }; F83ECEE52577EEF300DB18CB /* NSTracingReminderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTracingReminderViewController.swift; sourceTree = ""; }; @@ -1065,10 +1036,6 @@ F86A6A8926380C7E003CAC1B /* SwissCovid.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.protobuf; path = SwissCovid.proto; sourceTree = ""; }; F86A6A94263837AC003CAC1B /* NSReminderControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSReminderControl.swift; sourceTree = ""; }; F86A6A9726383803003CAC1B /* NSVenueView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSVenueView.swift; sourceTree = ""; }; - F87685B125B5F89900538B8B /* www.pt-a.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "www.pt-a.bfs.admin.ch.der"; sourceTree = ""; }; - F87685B225B5F89900538B8B /* www.pt-d.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "www.pt-d.bfs.admin.ch.der"; sourceTree = ""; }; - F87685B325B5F89A00538B8B /* www.pt-t.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "www.pt-t.bfs.admin.ch.der"; sourceTree = ""; }; - F87685B425B5F89A00538B8B /* www.pt.bfs.admin.ch.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = www.pt.bfs.admin.ch.der; sourceTree = ""; }; F87C369F258B6652008DCC81 /* ExposureNotification.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExposureNotification.framework; path = System/Library/Frameworks/ExposureNotification.framework; sourceTree = SDKROOT; }; F87C370D258C2613008DCC81 /* NSRadioButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSRadioButton.swift; sourceTree = ""; }; F87C370E258C2613008DCC81 /* NSRadioButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSRadioButtonGroup.swift; sourceTree = ""; }; @@ -2111,18 +2078,7 @@ F80E735C245ADEBC00C934B8 /* Certificates */ = { isa = PBXGroup; children = ( - 48E967992473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der */, - F80E7377245AFD2000C934B8 /* codegen-service-a.bag.admin.ch.der */, - F80E7379245AFD2000C934B8 /* codegen-service-d.bag.admin.ch.der */, - F80E7378245AFD2000C934B8 /* codegen-service.bag.admin.ch.der */, - 48E9679C2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der */, - F80E736E245AF38E00C934B8 /* www.pt1-a.bfs.admin.ch.der */, - F80E736F245AF38E00C934B8 /* www.pt1-d.bfs.admin.ch.der */, - F80E736D245AF38E00C934B8 /* www.pt1.bfs.admin.ch.der */, - F87685B125B5F89900538B8B /* www.pt-a.bfs.admin.ch.der */, - F87685B225B5F89900538B8B /* www.pt-d.bfs.admin.ch.der */, - F87685B325B5F89A00538B8B /* www.pt-t.bfs.admin.ch.der */, - F87685B425B5F89A00538B8B /* www.pt.bfs.admin.ch.der */, + AC35602827DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer */, ); path = Certificates; sourceTree = ""; @@ -2407,32 +2363,21 @@ buildActionMask = 2147483647; files = ( F8A6BE1E24C9BFA100DE6B44 /* FR_App_Datenschutzerklaerung.html in Resources */, - 242D2257245C4BD8005DAEA8 /* codegen-service-a.bag.admin.ch.der in Resources */, - 242D2258245C4BD8005DAEA8 /* codegen-service-d.bag.admin.ch.der in Resources */, - F87685B825B5F89B00538B8B /* www.pt-d.bfs.admin.ch.der in Resources */, - 242D2259245C4BD8005DAEA8 /* codegen-service.bag.admin.ch.der in Resources */, - 242D225A245C4BD8005DAEA8 /* www.pt1-a.bfs.admin.ch.der in Resources */, - 48E9679B2473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der in Resources */, - 242D225B245C4BD8005DAEA8 /* www.pt1-d.bfs.admin.ch.der in Resources */, - F87685BC25B5F89B00538B8B /* www.pt.bfs.admin.ch.der in Resources */, - 242D225C245C4BD8005DAEA8 /* www.pt1.bfs.admin.ch.der in Resources */, F8A6BE2224C9BFA100DE6B44 /* DE_App_Nutzungsbedingungen.html in Resources */, 242D225E245C4BD8005DAEA8 /* Inter-SemiBold.ttf in Resources */, F8A6BE1A24C9BFA100DE6B44 /* IT_App_Nutzungsbedingungen.html in Resources */, 242D225F245C4BD8005DAEA8 /* Inter-Bold.ttf in Resources */, - 48E9679E2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der in Resources */, F8A6BE1624C9BFA100DE6B44 /* EN_App_Datenschutzerklaerung.html in Resources */, F869AED524F54C7D00FC4556 /* Impressum in Resources */, F8A6BE2024C9BFA100DE6B44 /* EN_App_Nutzungsbedingungen.html in Resources */, 242D2260245C4BD8005DAEA8 /* Inter-Light.ttf in Resources */, F8A6BE1824C9BFA100DE6B44 /* IT_App_Datenschutzerklaerung.html in Resources */, - F87685B625B5F89B00538B8B /* www.pt-a.bfs.admin.ch.der in Resources */, - F87685BA25B5F89B00538B8B /* www.pt-t.bfs.admin.ch.der in Resources */, 242D2261245C4BD8005DAEA8 /* Assets.xcassets in Resources */, F8A6BE1C24C9BFA100DE6B44 /* DE_App_Datenschutzerklaerung.html in Resources */, 242D2262245C4BD8005DAEA8 /* InfoPlist.strings in Resources */, F8A6BE1424C9BFA100DE6B44 /* FR_App_Nutzungsbedingungen.html in Resources */, 242D2263245C4BD8005DAEA8 /* Inter-ExtraLight.ttf in Resources */, + AC35602A27DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer in Resources */, 242D2264245C4BD8005DAEA8 /* Launch Screen.storyboard in Resources */, F8B30B5A24D2C728006FBBBE /* DebugStrings.strings in Resources */, 242D2265245C4BD8005DAEA8 /* Inter-ExtraBold.ttf in Resources */, @@ -2469,32 +2414,21 @@ buildActionMask = 2147483647; files = ( F8A6BE1D24C9BFA100DE6B44 /* FR_App_Datenschutzerklaerung.html in Resources */, - F80E737A245AFE4500C934B8 /* codegen-service-a.bag.admin.ch.der in Resources */, - F80E737B245AFE4500C934B8 /* codegen-service-d.bag.admin.ch.der in Resources */, - F87685B725B5F89B00538B8B /* www.pt-d.bfs.admin.ch.der in Resources */, - F80E737C245AFE4500C934B8 /* codegen-service.bag.admin.ch.der in Resources */, - F80E7371245AF51C00C934B8 /* www.pt1-a.bfs.admin.ch.der in Resources */, - 48E9679A2473478100E4A5D6 /* codegen-service-t.bag.admin.ch.der in Resources */, - F80E7372245AF51C00C934B8 /* www.pt1-d.bfs.admin.ch.der in Resources */, - F87685BB25B5F89B00538B8B /* www.pt.bfs.admin.ch.der in Resources */, - F80E7373245AF51C00C934B8 /* www.pt1.bfs.admin.ch.der in Resources */, F8A6BE2124C9BFA100DE6B44 /* DE_App_Nutzungsbedingungen.html in Resources */, DCB4430E242DD34700F19AA5 /* Inter-SemiBold.ttf in Resources */, F8A6BE1924C9BFA100DE6B44 /* IT_App_Nutzungsbedingungen.html in Resources */, DCB44308242DD34700F19AA5 /* Inter-Bold.ttf in Resources */, - 48E9679D2473479100E4A5D6 /* www.pt1-t.bfs.admin.ch.der in Resources */, F8A6BE1524C9BFA100DE6B44 /* EN_App_Datenschutzerklaerung.html in Resources */, F8A6BE1F24C9BFA100DE6B44 /* EN_App_Nutzungsbedingungen.html in Resources */, DCB4430B242DD34700F19AA5 /* Inter-Light.ttf in Resources */, F8A6BE1724C9BFA100DE6B44 /* IT_App_Datenschutzerklaerung.html in Resources */, 91E1EBA424CB0A7100C5103D /* Impressum in Resources */, - F87685B525B5F89B00538B8B /* www.pt-a.bfs.admin.ch.der in Resources */, - F87685B925B5F89B00538B8B /* www.pt-t.bfs.admin.ch.der in Resources */, 8E81CCA2241FCC7F006F2437 /* Assets.xcassets in Resources */, F8A6BE1B24C9BFA100DE6B44 /* DE_App_Datenschutzerklaerung.html in Resources */, DC9A81AC242CE5E000AD1548 /* InfoPlist.strings in Resources */, F8A6BE1324C9BFA100DE6B44 /* FR_App_Nutzungsbedingungen.html in Resources */, DCB4430A242DD34700F19AA5 /* Inter-ExtraLight.ttf in Resources */, + AC35602927DB9DCE00FE4AD8 /* QuoVadis-Root-CA-2-G3.cer in Resources */, 24780B34242FDF4C003BB26C /* Launch Screen.storyboard in Resources */, F8B30B5924D2C728006FBBBE /* DebugStrings.strings in Resources */, DCB44309242DD34700F19AA5 /* Inter-ExtraBold.ttf in Resources */, diff --git a/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift b/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift index befb6a63d..e21e43596 100644 --- a/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift +++ b/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift @@ -83,7 +83,6 @@ class CertificateEvaluator: NSObject, URLSessionDelegate { let bundle = Bundle.main - // all these hosts have a seperate certificate let hosts = ["www.pt1.bfs.admin.ch", "www.pt1-d.bfs.admin.ch", "www.pt1-a.bfs.admin.ch", @@ -96,16 +95,18 @@ class CertificateEvaluator: NSObject, URLSessionDelegate { "www.pt-a.bfs.admin.ch", "www.pt-t.bfs.admin.ch", "www.pt.bfs.admin.ch"] - for host in hosts { - if let certificate = bundle.getCertificate(with: host) { - let evaluator = UBPinnedCertificatesTrustEvaluator(certificates: [certificate], - acceptSelfSignedCertificates: true, - performDefaultValidation: false, - validateHost: true) + + if let QuovadisRootCA = bundle.getCertificate(with: "QuoVadis-Root-CA-2-G3.cer") { + for host in hosts { + let evaluator = UBPinnedCertificatesTrustEvaluator(certificates: [QuovadisRootCA], + acceptSelfSignedCertificates: true, + performDefaultValidation: false, + validateHost: true) evaluators[host] = evaluator - } else { - assertionFailure("Could not load certificate for pinned host") + } + } else { + assertionFailure("Could not load certificate for pinned host") } return UBServerTrustManager(evaluators: evaluators) diff --git a/DP3TApp/Resources/Certificates/QuoVadis-Root-CA-2-G3.cer b/DP3TApp/Resources/Certificates/QuoVadis-Root-CA-2-G3.cer new file mode 100644 index 0000000000000000000000000000000000000000..3e49eba74782a8bcac1c87f76f3809872b98ddd0 GIT binary patch literal 1380 zcmXqLVofk;V)j_T%*4pVB;pcoq7vQMIotHpxgFZp(aY67lxi68vT19KA-KZ8LN z6Bknx6C=aIb*h^fCGLcG*t@;{x&F~><-$j^E+(ca#;;nh9{4m*TI`B$ZNP@$^1T&S z36D~kX4kaMz9!YT_P@Mn{ne>*LZ_dJw6Gk!Z?;)%e%IrZw`Z7~yJ0M@;r3_w$q$#d zT=nHF^8ER!{qf3MwF_&rH(9B!%nfo`^J8D$?>oziCr(IQ#w?=n>3RF&jEn)AMhC83rT-&ocbpMgx(TsPM6&FS@^c}vCCHz+Sv5~;EZPmkJCes&(2#TxgvC#E87KStJ|+r zW3>bYKUn*p%Kc(;r0~gqhNy2b>y#5(i$6XQ*r>Yh>ZIMm2YDV?-r8arzICpeb2ZWH@t!#jcLSb-knS{u>pOw5c7jEkKN91Qq@DOOgPk?}tZs{u2R zGT;LV@Phey9JPd()N{$kVR`FGO|>vl1&I-ccd zJ>d#R%%+5_2_H_JV@~?n{q}dB)*Uuq8%_RBv91Uo5B?V|iLY#mmX$woTGz4IIij*| z-KnL)yen-B_9{pwF3GX@<81Wsp!@$HZ$1ht8BLpW=v#E=v}y$&XUAF5-y$VD?uB-k z2Xa@$aLFxp`g?qC2mgUj6-+82lh=2)cbxQUkbQL_&n{8VY_}<&`n11kCf12X#UI6D z+}T1r8LA?LHgLUf{kw68acS0M8KGwharv_KC*_q-w3=If!j*)Sx4ih2Y)hR;xt$s@qw4|YSFQcHJR0iznZz8Nz9A$ znQ}C4-3z|B?)~4-K6ty3Pulohh{Qkseedg9vYj6n%(xv}x`{WEy;W4%^4iDyE7ka~ zc^u!oOzEq!dDK+*`PI85MDzb&6G>_Iyu_8Rc$?oeIYC3|@y4mmXIq!u)#rbn9+bCR zzqRJ8SMl?sb_ez<3bES%+0N|A6n#SV*`pAPua)gRvca4DTC3SCj_dsRv7x9}^=Wy~ z7iJlcWdZRW3I=(fnw(eLPH%Z0{9Ey$KfltfwjGgL-`s5_J9J;PKAiWal_P6oj>$>c UHy+y76>rmy^i^gy->dHd0ROW{V*mgE literal 0 HcmV?d00001 diff --git a/DP3TApp/Resources/Certificates/codegen-service-a.bag.admin.ch.der b/DP3TApp/Resources/Certificates/codegen-service-a.bag.admin.ch.der deleted file mode 100644 index 6314c9fb3ff15a2caf452ec14a27e5d0fe7d5d9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1990 zcmah}X;f3!7QXl9CIP}EVX_ejQyBu}BoIJLT|TBTzK0T&p_U;DA&>!*Fj$a6kV!-^ zP(*N`LKjtO`#=N{P>KtRAXSQhQh{1TACwjgMbu~UB^0ecTD`zJ^fjB zG!sBl@d=vxq3L|7l*|;TDP4qIk&GM?=ufi&)*BURnzF?lE{`j6lyW6$d^XoH)+sKQ z=M>8k@}Fb;r(?Yz#Q2|{_;Q|7H7qaK?^v7>hPl??etqc$r{_k-YEX|Z zJs@Iynf)yF^5>sw3u>U4V`V`mP`tLj^VCdmc}D)Y_B!Qdbi-!|De7I6gDgtUax@rKmK(na{t! z>^;|az~jndlp^!n;YG=}qBxiLNP*aYKhse&C^J~Cx)L%w7+5l7W1V0%S?9dJo%&l~ z=NSs-2Z09eJ?u)7s%)`fS%;c-_0oeotTvdq&PJS4#r~fFpTm57r4E~04bm($YjYos8pFG zRVs5zNfoE5=8?BCb53lrkeZSvl?r&yPHc$`*uNpd1*V%sFg}t7J3)_yJ#>rZQC&z8Vf892f?!z~!|k0vpm2JS1$LXtY>D7q%6~UO@RzhUYKZDTI_yP@4f}@6 zvbIe;D?JmU@&;Xin+dxTnaV(cR6mch&U|pQ8Wdvktob zh@sD)SvwjtF>#RmhqBW9*onsO4tB;b31n2T1&-heqj~Wcopy0iC zk$lWN?V~#w9hI|)PR#tvl-5rbn)mZRFDh=%$maz>D}nZzhzW`I<+N3kNA>zWE7R!m zCU{I?JU^rtb79hUFSE7l@o_vo?fm{{YaJI%phL?Br5?YstnAS}DQBw<|2rq188J00 zji^cUTvD_)5}ZE3l|;1l6doP3$ig>~SnGpNn+9@jgrD#j@6xHfnJ37|I!~pII1MIw zKR!L!J@n@adhu3Sbff>knxTVf1X=I?Pn~Ux+1||3#QGps`PRcrEj^~WO$=d0!<8?V c1a1o_+0B^s+`=P0>uZ{`UE3a#=tQIc0hnF!h5!Hn diff --git a/DP3TApp/Resources/Certificates/codegen-service-d.bag.admin.ch.der b/DP3TApp/Resources/Certificates/codegen-service-d.bag.admin.ch.der deleted file mode 100644 index 3478ba46261fb5090c3afcfd17678a9e4baccb4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1987 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 odGP(*A2nAGbw?ZZmwefjc|))%tgQXs+d zt1t+RAey${r>ZtpCiYD{K4@AnaUth{-J%-6<5Vb_UYQGc7*@kVz>lT|RB)Ie6h}Z@ z{b)p>vm#Up8v;b!yEP)ASkh@UI^EXFHbf!me@TWd03qOb)+anSFB%@Gt(YpSqpnPT+y|+&ChdXU>NM&M zS5aIfQxL`xlA?NKe3Exu6qnBl6S87SiUKK+$!5lKIUG?OD^{_{jU%~w2ht3H-s-^W z1hH65jTchGqXg8jNDeEG8W95|@TO1C0>(lZ3A`Rstg9jt7>2rHIj*@e!KwJd{Qf-Y zFU}dav9dX5{1uPk7sTb!_AMGd6Q#0%WE%>0`gpPdxtVvvtR%lNXeOj_yxI5ONiVU8 z=RbL`J(Z&5TJyRuJv|+@tre{zw)cWxm- zh0HXjk2-bEC0;PsTiD2^ZSBzdb+~ofZZ~7YnZ=|@6N3lF2nL4WY6->^NHFBph!_%3 z1VF%8aj1qSs4mbxrg?Q^PD`z?l}rnoJ@(s3V%8iXB>@O<%H}v2i&cVQG#W60%?AyT zO!F9_Fy79>LdX{hgjAl0o3N6x=+zuiBe)z3UV=ctj%H9J_(DMW*a&ADtu=BZ1o5Bg z14b)TX`nhlOY+&@9k5a-p+3M1c;M6(^PzBOFoZxvKqFK4bW|XkT75~jO z9MokGDz&^bU%L9Pyx=unb+(7LOfInO9uvQ%W-YlYxHZpiXB&zQqBB-UyrqyzqpNY9xRo*-Bf&XT&HH#n}S`anz#MM z{~P*MgKD|iHP;i9Kg+^dQ#@(lr&o#MTE$2Z7;3uc;jZMijqcN!zNkNI)O2a$M&-r2 z?M_d&usf8~ZdBYdKxD03u1OdW?ZA@bPXgm_Pt!AevHyZMT;{=FrzCYIov!|4|Bnne z^7V=$JIa%d@*OetrFO&b_C9?ZROx6LAP|dG7)#cn<$Vs5#HLN;fv-}Mi+vqCk8H#2 zt|JspGS0>HI`p#oh*^uWw8#0JvHeE#l}!s8F3!3?*A)@xv1Mbr9o_Q|+D*KsD`}#O zYTJI5I%3%OMqW^svfU+aQ>d) zN;>6lM;*wMZmE+S_WpaJo=9nNE(~nYc5(@0#H~|c61#KJa$w{C0ZE9@DPZ^41gX17mE@| zNMIyp3CtIPKFO4sA&g@saV7K!E}tvqBw=>IX0e1LQQwwAc*5N*9z8lbf*uwWKo4;T zR^~=-0C>1z81Tg0+!D3WZ3$uk10WF`u6f5VC=@dS28*J)hG-y1EO-NTmZ2~3S?b2n zGcZGt#F4Q0QhIU*M@$bBBn!oSmXw=D*B0n893Cf4$meGWxM|u=p@8lf7#8D%(ZI?@ zzJVb*TE>+~=pn*PtqY$ckkYqq4#TVgZD}GyicBVR6-lu*EZ5!15?59dpDS=>rve3R z`|f$ccnGI}O(5;QKB9nOsNl@;x!g|qhs9O<@`ifLs)=l@ocelLKx1@17{rvtCa1z( zsir3DNRb!XH;|6i)Nr~#UrG4%Ap!Jpc>`6-WxeM(*znOi0)4nUCV_~15jW)&7kK9$! z&}XN(IPvhA&RuH6FIV@AN4fFO19!J>)tm*1G&>7$Cw}z-5xg%T8h`#p;r~2YKDSkM zg{cYOJ9E`7Y$j{=`A!2h@_5|?HM3h&?hu0)v(ze^y-Yoty4kBnng1;9tO|36q)$iK z*{|YXwH)pF$+pEN0Kvf!d`5wD1PYw}QjBOM6af&h)}AZP4z&Q3L*zzce)GA_o~DVSyrulo%up(@Q2gB&XD&8QN5erV*hDbY zwur**2@sM5!(c7&c;kh@6vt(+#pJxFMKMPtlyId&v3x0GGzXLd$ZzO4_7RD5d#h5mW&SwNDz%g)*VaOM0I*StO8iTSygsDI$FzPbKA=46 z!KuXJiAPQ^5xT+MkV4yt%xb63gZhn4!!pYrmOp(1wf(y{8+cB$4kj!YDum^_58e}ZUy@Bp4V7_zc@wrR3J(A8AL)S)C}(_`Cw+SGcG3=&3X)?(9` z<-HQ9lDT3*wqljp0MY?D5sDggOT7k2%f4AlzS=No=e^|qyhyf>>+nzzuT1N}IgH@C zkA2!Tx?zW`w6e{1254W&^JSQ4*jjv?r5<~l%+f48x$=^4No$LWxe)$nG$-)GcW)Ez z?Tb?&Fyye-w|HvdT3^;bBWreed+XP$0`|oGuB*#EUqk%v1h2O0vB{Xf^*=s|)tjH| zzbA$l-2cc9IFRf8U!pYnWObhWD4`NN7$G5qioud4LE+k+#>T<`tn`r*PY zaY=TmUueEeeQ=>7!Yh8aKt(FgtsPpC>#nMd+u~;!?nb_% zAjMb|)$uTo<<)pv>c<#eNy#d8miT?sr>L&*fd*RZ^5d%3le{^5Kel&Hn7n+edxTg~ zc7)wNkxIOv%>JfD=CX6RYX6bUHvF$%-Ho3gWV&G(0MwH7V9sHf9DBW0)D^uvVJZe8^!7 ztwZFCgi02XLyc1kNvk4j`$~J+RjVeI^=*5}H>5s)^!@Q(*K_}V*L~l=-}Bzr1Elym zAjKAxVGtNW$h%62CUw3y+#tif5&5CCBsgM20U+X3Hp$#&b3_F_%L(0Q!q9j3)I{iy0{h367-s`FYVi7%nuX z4bWXnv*OaaU z)-Uqa2xPxFo=8Mv3M5JwJ~v8C+wJK=*8@6B8xbgRadDQ>V!8##G9*-F8O-7HqAc0r zKnibMd0#LV!bo8aNLg1!q%aJ%y9+ti4ZTK$JnPkg6nXIimyCbd;w z#R-2aa`t3)7pd>E8OP%Xb1A>fCvXBo&LkgCQq*8Xzt(lNEA$(jf|8r&d|gdTNCPdN z#xA}r9d5eZChis!NyT7UK&9U(GNdu*$n5;Z4ts6Kdk5lgk#KG!?sM6;6wU@DEnT-~ zo^&$j>31gOzczH0qw9!IAonD_wPP+U)4?piXFq$`Y@2p%Zf?W(xGk3Y4JIj()I5XCH+C1(@)q8iN;J+eB%aP7CGv^kj}dt) z+gX7%lxvg|UrQK+&4CC8hTw83#u!L321_y0(L@md0X^lt>KLHffRaLPAf;ca_Oxr) zf?lOOdl>)E8O5IFi#ZHi z%9Auf zj-VV7g|B}JAsiS6n}O{|F9fETEPE{`=QAS;xzPdM>+=ibb^8X zqN5&a1L(l2%%aRpX1c7*7Dj$f`G3ntevnp2DJVJMyid2L|6TVN>CSJvethRRz<3Y@ z)EA{SV0ZyVF$lIy8QWx>>e5al2?&ayQaDQa9frdp3FJ@464Z#iS@o-mwv1_h@I7y^!uKabQpKCl()5leh`Dq@_+wDRX!D=N&5X;+u|n2?>C;$vsJmXS#H?e#vPSY{y|h6PP2SIr}S+b zd)%&<5pyLs&)|oelix762IQ+=*fT}_{uF0(w(d`<%llqm;oH64qGq7Co;!A^PxbNo z()i!9tC~!38D7lh;Wdm6-ueiQGkc(tL(jB3+jhU~)WDTvp4@)FO53$kuC(oLkY+Nv z@-b09z8EP2Lxo}ATKsGN>{fP;^)~s=;%|%>aurl!`qLyQAK6gnc)F`Obmy(B+-ayj z<~e%=o78$UsMMghb2y~$0NETH?&ISpPNO>fFiAN~IuS@bI@#_+7*H2mDVqMRI76z$ zBs{YooE$SL86IcVt}CzHjUCPZ9p2jHcZ$UF-go?RFZ1zx=5-B!^Gv3BKq7L#rSEcn zzRRBs`RFf2!}DE}K2L8Ihw;J8snY5iX53U{oa5EjsdoLW%&OmNPq18c&z!bboE{x$ zk}{~fnb{i?bAM*Qta!BhIe%OEgOkJ!W#FK1x6c5(#diQ5lgktFJ zNZB85Z09yEQd>y%@YGJrYWH~LxaulCF)QjOQyRomprP9ftc%{KatL|1EB2?=8||MN zOz+uukV(zeRnePxF;qKIzvUQmsV8$jY^tR5@77d{aG!#MeNS@M+Eh%ihrRTV5LJ!K zhnwgBDow#Zlx8Bvc2f!DZTBac{GUCu8n59^>C>+n4zq`5DAA7uDG@YV7p+_CYBgGi zZc_`z^3-pW^^+Tir|I|<6)oRmmy#5c^?f{;G^cSSxBp(^ORuWbx*HunZ=N)@z2Ep! z#*!a4_b_~+s1Erfb=ny_^~klfT^>J_L3(p+eT3<&KD+*B&0zwy01m#6kdkXEh`3tN HTvq&FyWQ_b diff --git a/DP3TApp/Resources/Certificates/www.pt1-t.bfs.admin.ch.der b/DP3TApp/Resources/Certificates/www.pt1-t.bfs.admin.ch.der deleted file mode 100644 index 3a521cbfe2d206e52b9f48f6b8152700e4457141..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1971 zcmZ{kdpOi-8^?dY-~0v{hh-cZ!el74#xoeAceObmIw3i1tHlh4IWQ;9#-^!ARJe=r%^~??NSJX*ZUCiU~fot^bx3djLYf!3!VpTs$eN0Jl_BNd_`rm6~<@866FEnE;^GQ%VDtuY(}j7ki#aLIeYCgrx*bJ zrF@)bm=gNo}hM^AMw?ihOv_GqR`nO!5zRr5=^7(zMw+)+Iv}L<&6dh{jlsFUK%PM5xTiud`AarMlx(cYS;Z^~z%*z6Bf51gz^vXagv zm(})-6*Du^v!xsHuGr&KKEC3k&m5j6B{GGXjc28cPNOrr7Fjdb#C^0kj!M18ggK6# zgP;AY;IBJK#p1y3K?hEk#17nka9ii+fw`K5>HCLA>eE^>YTg;Xm3iN}BjFaUTQCZ5 z^}C**9!T0$upgK0C#XD+t*?BtOKsuDJ?xBx)}3_s%dc{DPYoIeb@u;h*r2=Fve&Ht zjPzv1UvENesOzu?s-9&u;j$1648cWW%sL>(7%$t%0FNR70!H$CH6Wp@fljj4CC#+@ zavy4=HZ+ku_F!M?BtB6HcnV4z6kse?5r)wyKnM2kH9%4}MDzJ^j#gHDu7JlU#|t>Z zB_BpDdyO2yVOhlsc|2y64LO3#2NoX^;S|G_L{0=R?h|>-{x#4*^}uTJ|9f%YrN)V< zH}C}R3M%pzQMf4xLTE4y?11fi76R*-t+1Ay^NAL@^f(TW!RK&A%N{c@MRkDI`*aNQ zVFv@pC87~(11P|%(6Vqtiup?A2rlzemH!!@`FCkGR0q}4p&HF^$jlAbJlA{3X{6Zw z=Ic=zP+Kak4#SHmib1f2a@!^;C@;^{CqhsJ6~ip~V!%<50NSmkkZ*Or?b+R#N7r0y zoh0tUhQ>UNJuNRz<_7eo-H|~M96!{t*9U@74CD@65h63^O6g@-Ht#~^Bsmo2G{hg* zco}`FubsHws61M8UJD^=wAso6N0>+SbB(oU_@v#{HK%Oyg41}F6*CW~Aya=y2yy&{ zm6Qnz;Xxi$?5n+)puI*zinK!J3K_LZ`Sw!$3bt+;>jB&lqG5F!^he?E)g~8%OBM!N zzY5(EePDdJZQ?rm>)DJ;?IdI^l*wnWkMv_)jGyeBOAT(H4x$~}`Xwt;TmNdI;`brz zhUDC<(t$>!WAXLbCrLRKKkWC|7aho!WSm}jjq3i?nJkLB z>}U7**?hMWX0_u&OLUVcKKXgCipZcd)_e8@VThmAe8Mm@oS%9z+w``_l|d9g-8Jrg zD2x|Q*gU*?-{Pa4{N~>+hQ(74&0|ZRES}%)JkcG!SugHPnP`7DAT@cH0;oO*a*S*B9;2WauTHKnuMecc) zqlLG62u$|D_WwOA42pgpi^FUh}&})bh{{tVX3z+}_ diff --git a/DP3TApp/Resources/Certificates/www.pt1.bfs.admin.ch.der b/DP3TApp/Resources/Certificates/www.pt1.bfs.admin.ch.der deleted file mode 100644 index 02d314aae8ee596f0a2e34e8f6400b64d650d28b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1968 zcmZuxX;f3^7QHvQB#49bj@Q3kb85<-LwWI|lX5Nr^T>H~{J zC{RIaUw9x0R13;bi=ZeE-jnGekwKAKfjXloO%Qzk@W);2?(f{Q_de(D^#L)q28dNo zRiIHY3Pnuy&vrgP`Mk37&S|Ym*k}e4C7TG;7`E=H4Hiw!4Obx z{uBeSP6==X?LZ+XEQZG9ll+)0rhrbPm;z&E2~Al4q2!s!`6ecj6ddeF@^*J6c~XJV zIt@Dj?CmHNU{3|xBNfo@J#?S~5D7AW{z%W=i=qMWN>WvI#Es5luR-i8bOufayCr6}EVbR$F(pSFTYh|=LF*!NemMfsxeiO&HjiIraY}?p)Acjpo-WIF^ zp~bKkq&UZ+#4rrWi`ge<>YbwzI<-tBrk@a=_1du^Vj z7pE`csvtpA>z+f5k%E-yr~$2Q1KaaX=Qbux-yL2Gk{Qy1(H(+jLb-zU2^O~Ftr zDMIKk>DBhJ(MpP<`xMgrVqND2ne5gsol$CBwRYLjU*>!5J0F`IE^@Yd6>1K!6Y>SNNkWcDnL@@&(rjZn zEOL^F&rf7fZDV-?VELgDmKd!za$@=1PxQTarK5Bu{`CJ2RN5vXKEMljVAK@`5%_92 zgrdPP*aGa=lu)qV=2azSYCd6*N9S_*OaX_N0t}UuW{4gjuC=34AKqZ#s4QVr}u-n`B)cyE|l+fwRT z(QCygr>ZUl$+PiikKU|)`&<477w#R4zHQrF?VFl<;WD>FBuEH_5iw`TL!E#A=lR5y zT@B9HJw%sVr8@iC=T8-f4(9y(?y4|vMc4!ELXrIG?$hP%R%W4$9s^dkuBy#>^o>Yz z^p&?`9@8%8CQDGHBcol0?-;Y2?xlh3RMN_E+^}Lt}}w_fGjZi4XS6hju2L5$5v!L!EO{O-eAhvPh4QeZRse$wjn+_ z%Psl=spH0ct1xSM=a+?wal4B)ZmM_-9}m2-FczRTm?|8vy*1!)spX>k+}BcCch_tj=7AM0sO`2?7d(ER7=JD1Y9 z_O;Cek$?R`l#LB(YlTKl5Qz6{ZoLa597(g=QX_TDy5abJYh45gtf6F(7LAatAfb{d!Qhxlc(!79cj~^3NS_J&`MV zT-Xp1-iSUtIQZWUrt#!dWJs2C)SZ8w>hCkK8`9ClTG~f5I z#@eau&77rcv*~Q*3j!`&W4TV+h@T$`){;g4>f12bS;6hN5v@Xe@Oo$MzwGw+_`Fzr zt3M;T6xVd!a_&2t>!NJ5U$&*MHEHzD)}iw9>>9b}{rtcaFS|6F0-w%ZXe-;aF8Flb zH9__dwI(~%LOM%MPg7;3`xj$9v}Pw0&B(L!ml=e5nuJVBZ From 8e1ed3ef62bad757caf8d10bf95b7aff0563cf97 Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Mon, 14 Mar 2022 09:22:09 +0100 Subject: [PATCH 06/28] fixes loading of certificate --- DP3TApp/Logic/Networking/Base/URLSession+pinning.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift b/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift index e21e43596..67541b72a 100644 --- a/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift +++ b/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift @@ -96,7 +96,7 @@ class CertificateEvaluator: NSObject, URLSessionDelegate { "www.pt-t.bfs.admin.ch", "www.pt.bfs.admin.ch"] - if let QuovadisRootCA = bundle.getCertificate(with: "QuoVadis-Root-CA-2-G3.cer") { + if let QuovadisRootCA = bundle.getCertificate(with: "QuoVadis-Root-CA-2-G3", fileExtension: "cer") { for host in hosts { let evaluator = UBPinnedCertificatesTrustEvaluator(certificates: [QuovadisRootCA], acceptSelfSignedCertificates: true, From cebdf47196fa4eb4a4a92c8ddc3982445aee9512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Mon, 14 Mar 2022 15:30:05 +0100 Subject: [PATCH 07/28] fix build, fix authorization issue --- DP3TApp.xcodeproj/project.pbxproj | 4 ++++ DP3TApp/Logic/Config/ConfigManager.swift | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/DP3TApp.xcodeproj/project.pbxproj b/DP3TApp.xcodeproj/project.pbxproj index 208c5dbed..17619bc59 100644 --- a/DP3TApp.xcodeproj/project.pbxproj +++ b/DP3TApp.xcodeproj/project.pbxproj @@ -508,6 +508,8 @@ DCE1398E26382B5E0093D8F6 /* CreatedEventsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE1398C26382B5E0093D8F6 /* CreatedEventsManager.swift */; }; DCE32DF52637D05F00168693 /* QRCodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE32DF42637D05F00168693 /* QRCodeTests.swift */; }; DF3BA22324520EE3009086E7 /* NSDebugDatabaseUploadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3BA22224520EE3009086E7 /* NSDebugDatabaseUploadHelper.swift */; }; + F20EEE3627DF889D004DADA2 /* NSDeactivatedInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F223579F27D60473001EFCD6 /* NSDeactivatedInfoViewController.swift */; }; + F20EEE3727DF88A1004DADA2 /* NSDeactivatedInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F223579D27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift */; }; F223579E27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F223579D27D5F7BC001EFCD6 /* NSDeactivatedInfoView.swift */; }; F22357A027D60473001EFCD6 /* NSDeactivatedInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F223579F27D60473001EFCD6 /* NSDeactivatedInfoViewController.swift */; }; F806D33C24F91C7800672DFC /* LocalPushProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F806D33B24F91C7800672DFC /* LocalPushProtocol.swift */; }; @@ -2775,6 +2777,7 @@ 246FD176249B568F00FD36F8 /* Environment+AppStore.swift in Sources */, 242D220B245C4BD8005DAEA8 /* NSCovidCodeInfoViewController.swift in Sources */, 6EF28D9226393878001C1565 /* QRCodePDFGenerator.swift in Sources */, + F20EEE3727DF88A1004DADA2 /* NSDeactivatedInfoView.swift in Sources */, F87C3713258C2613008DCC81 /* NSRadioButtonGroup.swift in Sources */, 242D220C245C4BD8005DAEA8 /* RandomGenerators.swift in Sources */, 6EF28D052637053C001C1565 /* NSQRScanningView.swift in Sources */, @@ -2836,6 +2839,7 @@ 242D2223245C4BD8005DAEA8 /* ConfigResponseBody.swift in Sources */, F86A6A772637F226003CAC1B /* CheckInExposure.swift in Sources */, F8A6BE2B24CAC2C700DE6B44 /* NSInfoBoxVisibilityManager.swift in Sources */, + F20EEE3627DF889D004DADA2 /* NSDeactivatedInfoViewController.swift in Sources */, 242D2224245C4BD8005DAEA8 /* NSLayoutConstants.swift in Sources */, 2411CA9D245F1085002FB5A9 /* Environment+PublicKeys.swift in Sources */, F83ECEE72577EEF300DB18CB /* NSTracingReminderViewController.swift in Sources */, diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 206bb9467..04d9a8389 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -224,7 +224,7 @@ class ConfigManager: NSObject { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } appDelegate.window?.rootViewController? = vc } - + if TracingManager.shared.isActivated { TracingManager.shared.endTracing() } @@ -235,8 +235,10 @@ class ConfigManager: NSObject { if !UserStorage.shared.appDeactivated { UserStorage.shared.appDeactivated = true } - } else if !config.deactivate && UserStorage.shared.appDeactivated { - TracingManager.shared.startTracing() + } else if !config.deactivate, UserStorage.shared.appDeactivated { + if TracingManager.shared.isAuthorized { + TracingManager.shared.startTracing() + } TracingManager.shared.setBackgroundRefreshEnabled(true) UserStorage.shared.appDeactivated = false From bc41a5ccc2ca8706209118184403061460011d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Wed, 16 Mar 2022 09:08:43 +0100 Subject: [PATCH 08/28] Bump version to 2.3.2 --- DP3TApp/Supporting Files/Info.plist | 2 +- DP3TAppClip/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DP3TApp/Supporting Files/Info.plist b/DP3TApp/Supporting Files/Info.plist index dfbf5aae1..440d06873 100644 --- a/DP3TApp/Supporting Files/Info.plist +++ b/DP3TApp/Supporting Files/Info.plist @@ -24,7 +24,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.3.0 + 2.3.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) ENAPIVersion diff --git a/DP3TAppClip/Info.plist b/DP3TAppClip/Info.plist index 0b085fd86..0135be819 100644 --- a/DP3TAppClip/Info.plist +++ b/DP3TAppClip/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.3.0 + 2.3.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS From 0a56fbdce3cb8fdad1365a65231cb317e1c5f38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Wed, 16 Mar 2022 17:09:09 +0100 Subject: [PATCH 09/28] fix possible null values in infobox-config --- DP3TApp/Logic/Config/ConfigResponseBody.swift | 2 +- DP3TApp/Logic/Tracing/UIState/UIStateLogic.swift | 4 ++-- DP3TApp/Screens/WhatToDo/NSWhatToDoInformModuleView.swift | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DP3TApp/Logic/Config/ConfigResponseBody.swift b/DP3TApp/Logic/Config/ConfigResponseBody.swift index 2277afaf6..dab41e2b6 100644 --- a/DP3TApp/Logic/Config/ConfigResponseBody.swift +++ b/DP3TApp/Logic/Config/ConfigResponseBody.swift @@ -52,7 +52,7 @@ class ConfigResponseBody: UBCodable { public let deactivationMessage: LocalizedValue? class InfoBox: UBCodable { - let title, msg: String + let title, msg: String? let url: URL? let urlTitle: String? let infoId: String? diff --git a/DP3TApp/Logic/Tracing/UIState/UIStateLogic.swift b/DP3TApp/Logic/Tracing/UIState/UIStateLogic.swift index d76082d78..f85da82e8 100644 --- a/DP3TApp/Logic/Tracing/UIState/UIStateLogic.swift +++ b/DP3TApp/Logic/Tracing/UIState/UIStateLogic.swift @@ -311,8 +311,8 @@ class UIStateLogic { private func setInfoBoxState(_ newState: inout UIStateModel) { if let infoBox = ConfigManager.currentConfig?.infoBox?.value, infoBox.infoId == nil || !NSInfoBoxVisibilityManager.shared.dismissedInfoBoxIds.contains(infoBox.infoId!) { - newState.homescreen.infoBox = UIStateModel.Homescreen.InfoBox(title: infoBox.title, - text: infoBox.msg, + newState.homescreen.infoBox = UIStateModel.Homescreen.InfoBox(title: infoBox.title ?? "", + text: infoBox.msg ?? "", link: infoBox.urlTitle, url: infoBox.url, isDismissible: infoBox.isDismissible, diff --git a/DP3TApp/Screens/WhatToDo/NSWhatToDoInformModuleView.swift b/DP3TApp/Screens/WhatToDo/NSWhatToDoInformModuleView.swift index 17a914622..5c8ca2088 100644 --- a/DP3TApp/Screens/WhatToDo/NSWhatToDoInformModuleView.swift +++ b/DP3TApp/Screens/WhatToDo/NSWhatToDoInformModuleView.swift @@ -60,8 +60,8 @@ class NSWhatToDoInformModuleView: NSSimpleModuleBaseView { self.hearingImpairedButtonTouched?() } } - var model = NSInfoBoxView.ViewModel(title: infoBox.title, - subText: infoBox.msg, + var model = NSInfoBoxView.ViewModel(title: infoBox.title ?? "", + subText: infoBox.msg ?? "", titleColor: .ns_text, subtextColor: .ns_text, additionalText: infoBox.urlTitle, From 897d9bfa51d74adfb67841087849bed8c00e6806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Thu, 17 Mar 2022 12:37:32 +0100 Subject: [PATCH 10/28] bump version to 2.4.0 --- DP3TApp/Supporting Files/Info.plist | 2 +- DP3TAppClip/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DP3TApp/Supporting Files/Info.plist b/DP3TApp/Supporting Files/Info.plist index 440d06873..49c72670c 100644 --- a/DP3TApp/Supporting Files/Info.plist +++ b/DP3TApp/Supporting Files/Info.plist @@ -24,7 +24,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.3.2 + 2.4.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) ENAPIVersion diff --git a/DP3TAppClip/Info.plist b/DP3TAppClip/Info.plist index 0135be819..b69bc5c80 100644 --- a/DP3TAppClip/Info.plist +++ b/DP3TAppClip/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.3.2 + 2.4.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS From 8452f644a7979f69b35c66ebf8b1769d754d6d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Thu, 17 Mar 2022 13:24:21 +0100 Subject: [PATCH 11/28] fix and add tests according to new ConfigResponse --- DP3TAppTests/ConfigResponseBodyTests.swift | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/DP3TAppTests/ConfigResponseBodyTests.swift b/DP3TAppTests/ConfigResponseBodyTests.swift index 49f10ae17..46cf3f050 100644 --- a/DP3TAppTests/ConfigResponseBodyTests.swift +++ b/DP3TAppTests/ConfigResponseBodyTests.swift @@ -14,15 +14,22 @@ import XCTest class ConfigResponseBodyTests: XCTestCase { func testParsing() { let json = """ - {"forceUpdate": false,"forceTraceShutdown": false,"infoBox": {"deInfoBox": {"title": "Hinweis","msg": "Info box body","url": "https://www.bag.admin.ch/","urlTitle": "Weitere Informationen"},"frInfoBox": null,"itInfoBox": null,"enInfoBox": null,"ptInfoBox": null,"esInfoBox": null,"sqInfoBox": null,"bsInfoBox": null,"hrInfoBox": null,"srInfoBox": null,"rmInfoBox": null},"sdkConfig": {"numberOfWindowsForExposure": 3,"eventThreshold": 0.8,"badAttenuationThreshold": 73,"contactAttenuationThreshold": 73},"iOSGaenSdkConfig": {"lowerThreshold": 53,"higherThreshold": 60,"factorLow": 1,"factorHigh": 0.5,"triggerThreshold": 15},"androidGaenSdkConfig": {"lowerThreshold": 53,"higherThreshold": 60,"factorLow": 1,"factorHigh": 0.5,"triggerThreshold": 15}, "interOpsCountries": ["CH", "LI", "DE"], "showVaccinationInfo": true, "vaccinationBookingInfo": {} } + {"forceUpdate": false,"forceTraceShutdown": false,"infoBox": {"deInfoBox": {"title": "Hinweis","msg": "Info box body","url": "https://www.bag.admin.ch/","urlTitle": "Weitere Informationen"},"frInfoBox": null,"itInfoBox": null,"enInfoBox": null,"ptInfoBox": null,"esInfoBox": null,"sqInfoBox": null,"bsInfoBox": null,"hrInfoBox": null,"srInfoBox": null,"rmInfoBox": null},"sdkConfig": {"numberOfWindowsForExposure": 3,"eventThreshold": 0.8,"badAttenuationThreshold": 73,"contactAttenuationThreshold": 73},"iOSGaenSdkConfig": {"lowerThreshold": 53,"higherThreshold": 60,"factorLow": 1,"factorHigh": 0.5,"triggerThreshold": 15},"androidGaenSdkConfig": {"lowerThreshold": 53,"higherThreshold": 60,"factorLow": 1,"factorHigh": 0.5,"triggerThreshold": 15}, "interOpsCountries": ["CH", "LI", "DE"], "showVaccinationInfo": true, "vaccinationBookingInfo": {}, "deactivate": false, "deactivationMessage": {"deInfoBox": {"title": "Deaktiviert","msg": "Deactivation Message Body","url": "https://www.bag.admin.ch/deactivated","urlTitle": "Weitere Informationen zur Deaktivierung"},"frInfoBox": null,"itInfoBox": null,"enInfoBox": null,"ptInfoBox": null,"esInfoBox": null,"sqInfoBox": null,"bsInfoBox": null,"hrInfoBox": null,"srInfoBox": null,"rmInfoBox": null}} """ let config = try! JSONDecoder().decode(ConfigResponseBody.self, from: json.data(using: .utf8)!) + XCTAssertEqual(config.forceUpdate, false) XCTAssertEqual(config.infoBox?.value(for: "de")?.title, "Hinweis") XCTAssertEqual(config.infoBox?.value(for: "de")?.msg, "Info box body") XCTAssertEqual(config.infoBox?.value(for: "de")?.url?.absoluteString, "https://www.bag.admin.ch/") XCTAssertEqual(config.infoBox?.value(for: "de")?.urlTitle, "Weitere Informationen") + XCTAssertEqual(config.deactivate, false) + XCTAssertEqual(config.deactivationMessage?.value(for: "de")?.title, "Deaktiviert") + XCTAssertEqual(config.deactivationMessage?.value(for: "de")?.msg, "Deactivation Message Body") + XCTAssertEqual(config.deactivationMessage?.value(for: "de")?.url?.absoluteString, "https://www.bag.admin.ch/deactivated") + XCTAssertEqual(config.deactivationMessage?.value(for: "de")?.urlTitle, "Weitere Informationen zur Deaktivierung") + XCTAssertNil(config.infoBox?.value(for: "fr")) XCTAssertNil(config.infoBox?.value(for: "it")) XCTAssertNil(config.infoBox?.value(for: "en")) @@ -34,6 +41,17 @@ class ConfigResponseBodyTests: XCTestCase { XCTAssertNil(config.infoBox?.value(for: "sr")) XCTAssertNil(config.infoBox?.value(for: "rm")) + XCTAssertNil(config.deactivationMessage?.value(for: "fr")) + XCTAssertNil(config.deactivationMessage?.value(for: "it")) + XCTAssertNil(config.deactivationMessage?.value(for: "en")) + XCTAssertNil(config.deactivationMessage?.value(for: "pt")) + XCTAssertNil(config.deactivationMessage?.value(for: "es")) + XCTAssertNil(config.deactivationMessage?.value(for: "sq")) + XCTAssertNil(config.deactivationMessage?.value(for: "bs")) + XCTAssertNil(config.deactivationMessage?.value(for: "hr")) + XCTAssertNil(config.deactivationMessage?.value(for: "sr")) + XCTAssertNil(config.deactivationMessage?.value(for: "rm")) + XCTAssertEqual(config.iOSGaenSdkConfig?.factorHigh, 0.5) XCTAssertEqual(config.iOSGaenSdkConfig?.lowerThreshold, 53) XCTAssertEqual(config.iOSGaenSdkConfig?.higherThreshold, 60) From 8fbb735f7839ef3f306bfa6fdb5e5c77a402fe3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Thu, 17 Mar 2022 15:42:39 +0100 Subject: [PATCH 12/28] Explicitly cancel BG Tasks, Load config on every appstart --- DP3TApp.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- DP3TApp/Logic/AppDelegate.swift | 4 ++-- .../Logic/Config/ConfigLoadOperation.swift | 21 ++++++++++++++----- DP3TApp/Logic/Config/ConfigManager.swift | 3 +++ 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/DP3TApp.xcodeproj/project.pbxproj b/DP3TApp.xcodeproj/project.pbxproj index 17619bc59..42c709b17 100644 --- a/DP3TApp.xcodeproj/project.pbxproj +++ b/DP3TApp.xcodeproj/project.pbxproj @@ -4731,7 +4731,7 @@ repositoryURL = "https://github.com/DP-3T/dp3t-sdk-ios.git"; requirement = { kind = exactVersion; - version = 2.5.0; + version = 2.5.1; }; }; F870A5B52492C6D500C34FFA /* XCRemoteSwiftPackageReference "SQLite" */ = { diff --git a/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 485fc949c..59b9e9dee 100644 --- a/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DP3TApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/DP-3T/dp3t-sdk-ios.git", "state": { "branch": null, - "revision": "69d3e7863a7d8f9c230745b705481f2ade461e10", - "version": "2.5.0" + "revision": "002a66ec4a6564c3803d1410e955eabdf0556a3c", + "version": "2.5.1" } }, { diff --git a/DP3TApp/Logic/AppDelegate.swift b/DP3TApp/Logic/AppDelegate.swift index 0eacbd72f..2cc94b515 100644 --- a/DP3TApp/Logic/AppDelegate.swift +++ b/DP3TApp/Logic/AppDelegate.swift @@ -114,8 +114,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.makeKeyAndVisible() - ConfigManager().startConfigRequest(window: window) - let deactivated = UserStorage.shared.appDeactivated guard !deactivated else { return } @@ -139,6 +137,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidBecomeActive(_: UIApplication) { // Start sync after app became active TracingManager.shared.updateStatus(shouldSync: true, completion: nil) + + ConfigManager().startConfigRequest(window: window) } private func willAppearAfterColdstart(_: UIApplication, coldStart: Bool, backgroundTime: TimeInterval) { diff --git a/DP3TApp/Logic/Config/ConfigLoadOperation.swift b/DP3TApp/Logic/Config/ConfigLoadOperation.swift index 17991a1d9..4ea140677 100644 --- a/DP3TApp/Logic/Config/ConfigLoadOperation.swift +++ b/DP3TApp/Logic/Config/ConfigLoadOperation.swift @@ -34,14 +34,25 @@ class ConfigLoadOperation: Operation { ConfigLoadOperation.presentedConfigForVersion = ConfigManager.appVersion } } else if let c = config, c.deactivate { - let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) - vc.modalPresentationStyle = .fullScreen + DispatchQueue.main.sync { + let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.window?.rootViewController?.present(vc, animated: true, completion: nil) + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + appDelegate.window?.rootViewController? = vc + } + + if TracingManager.shared.isActivated { + TracingManager.shared.endTracing() + } - TracingManager.shared.endTracing() + TracingManager.shared.setBackgroundRefreshEnabled(false) + UBPushManager.shared.setActive(false) CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) + + if !UserStorage.shared.appDeactivated { + UserStorage.shared.appDeactivated = true + } + } else { self.cancel() } diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 04d9a8389..371610c81 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -81,6 +81,9 @@ class ConfigManager: NSObject { } static func shouldLoadConfig(backgroundTask: Bool, url: String?, lastConfigUrl: String?, lastConfigLoad: Date?) -> Bool { + if ConfigManager.currentConfig?.deactivate ?? false { + return true + } // if the config url was changed (by OS version or app version changing) load config in anycase if url != lastConfigUrl { return true From 9bf494929af151051ee6c565eaf715b4df690ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Thu, 17 Mar 2022 16:24:10 +0100 Subject: [PATCH 13/28] update translations, use correct header --- .../Deactivated/NSDeactivatedInfoView.swift | 2 +- Translations/bs.lproj/Localizable.strings | 15 +++++++++++++++ Translations/de.lproj/Localizable.strings | 15 +++++++++++++++ Translations/en.lproj/Localizable.strings | 15 +++++++++++++++ Translations/es.lproj/Localizable.strings | 15 +++++++++++++++ Translations/fr.lproj/Localizable.strings | 15 +++++++++++++++ Translations/hr.lproj/Localizable.strings | 15 +++++++++++++++ Translations/it.lproj/Localizable.strings | 15 +++++++++++++++ Translations/pt.lproj/Localizable.strings | 15 +++++++++++++++ Translations/rm.lproj/Localizable.strings | 15 +++++++++++++++ Translations/sq.lproj/Localizable.strings | 15 +++++++++++++++ Translations/sr-Latn-RS.lproj/Localizable.strings | 15 +++++++++++++++ Translations/ti.lproj/Localizable.strings | 15 +++++++++++++++ Translations/tr.lproj/Localizable.strings | 15 +++++++++++++++ 14 files changed, 196 insertions(+), 1 deletion(-) diff --git a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift index 23238a33c..018f36a78 100644 --- a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift +++ b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift @@ -21,7 +21,7 @@ class NSDeactivatedInfoView: NSSimpleModuleBaseView { externalLinkButton = NSExternalLinkButton(style: .normal(color: .ns_blue), size: .normal, linkType: .url) super.init(title: ConfigManager.currentConfig?.deactivationMessage?.value?.title ?? "", - subtitle: "accessibility_info_button".ub_localized, + subtitle: "termination_header".ub_localized, subview: illuView, text: ConfigManager.currentConfig?.deactivationMessage?.value?.msg ?? "", subtitleColor: .ns_blue, diff --git a/Translations/bs.lproj/Localizable.strings b/Translations/bs.lproj/Localizable.strings index 2a35432fc..e82214696 100644 --- a/Translations/bs.lproj/Localizable.strings +++ b/Translations/bs.lproj/Localizable.strings @@ -1909,3 +1909,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Zakažite sada termin"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Informacije"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "Rad aplikacije SvissCovid je obustavljen"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Hvala vam što ste preuzeli aplikaciju SwissCovid.\n\nKorišćenjem aplikacije SwissCovid dali ste relevantan doprinos u borbi protiv pandemije. Više od 175.000 ljudi bilo je u mogućnosti da brzo informiše druge o riziku od infekcije i tako prekine lance infekcije.\n\nUkidanjem saveznih mera obustavljen je i rad aplikacije SwissCovid. Aplikacija je sada neaktivna i trebalo bi da je deinstalirate. Ukoliko bi reaktivacija bila neophodna zbog epidemiološke situacije, ponovo se radujemo vašoj podršci."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Hvala vam što ste preuzeli aplikaciju SwissCovid.\n\nKorišćenjem aplikacije SwissCovid dali ste relevantan doprinos u borbi protiv pandemije. Više od 175.000 ljudi bilo je u mogućnosti da brzo informiše druge o riziku od infekcije i tako prekine lance infekcije.\n\nUkidanjem saveznih mera obustavljen je i rad aplikacije SwissCovid. Aplikacija je sada neaktivna i trebalo bi da je deinstalirate. Ukoliko bi reaktivacija bila neophodna zbog epidemiološke situacije, ponovo se radujemo vašoj podršci."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Dalje informacije"; diff --git a/Translations/de.lproj/Localizable.strings b/Translations/de.lproj/Localizable.strings index 0f719d3a5..ebddf2755 100644 --- a/Translations/de.lproj/Localizable.strings +++ b/Translations/de.lproj/Localizable.strings @@ -1993,3 +1993,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Jetzt Termin buchen"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Information"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "Der Betrieb der SwissCovid App wurde eingestellt"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Vielen Dank, dass Sie die SwissCovid App heruntergeladen haben.\n\nMit der Verwendung der SwissCovid App haben Sie einen relevanten Beitrag zur Pandemiebekämpfung geleistet. Über 175’000 Personen konnten andere schnell über ein Ansteckungsrisiko informieren und somit Infektionsketten unterbrechen.\n\nMit der Aufhebung der Massnahmen des Bundes wurde auch der Betrieb der SwissCovid App eingestellt. Die App ist per sofort inaktiv und soll deinstalliert werden. Sollte eine Reaktivierung aufgrund der epidemiologischen Situation nötig sein, würden wir uns wieder über Ihre Unterstützung freuen."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Vielen Dank, dass Sie die SwissCovid App heruntergeladen haben.\n\nMit der Verwendung der SwissCovid App haben Sie einen relevanten Beitrag zur Pandemiebekämpfung geleistet. Über 175’000 Personen konnten andere schnell über ein Ansteckungsrisiko informieren und somit Infektionsketten unterbrechen.\n\nMit der Aufhebung der Massnahmen des Bundes wurde auch der Betrieb der SwissCovid App eingestellt. Die App ist per sofort inaktiv und soll deinstalliert werden. Sollte eine Reaktivierung aufgrund der epidemiologischen Situation nötig sein, würden wir uns wieder über Ihre Unterstützung freuen."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Weitere Informationen"; diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index 251a9f52a..9c13a460f 100644 --- a/Translations/en.lproj/Localizable.strings +++ b/Translations/en.lproj/Localizable.strings @@ -1937,3 +1937,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Book an appointment now"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Information"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "The SwissCovid app has been discontinued"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Thank you for downloading the SwissCovid app.\n\nYou have made an important contribution to fighting the pandemic by using the SwissCovid app. More than 175,000 people were able to break the transmission chain by quickly informing others of the risk of infection.\n\nThe SwissCovid app was discontinued when the federal government's other measures were lifted. The app is now inactive and you should delete it. We hope we can count on your support again if the epidemiological situation should require its reactivation in the future."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Thank you for downloading the SwissCovid app.\n\nYou have made an important contribution to fighting the pandemic by using the SwissCovid app. More than 175,000 people were able to break the transmission chain by quickly informing others of the risk of infection.\n\nThe SwissCovid app was discontinued when the federal government's other measures were lifted, and it should now be deleted. We hope we can count on your support again if the epidemiological situation should require its reactivation in the future."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Further information"; diff --git a/Translations/es.lproj/Localizable.strings b/Translations/es.lproj/Localizable.strings index a87e608c7..45982d05f 100644 --- a/Translations/es.lproj/Localizable.strings +++ b/Translations/es.lproj/Localizable.strings @@ -1909,3 +1909,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Concierte su cita ahora"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Información"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "La app SwissCovid ya no está operativa"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Muchas gracias por descargar la aplicación SwissCovid.\n\nCon la utilización de la app SwissCovid ha realizado usted un valioso aporte a la lucha contra la pandemia. Más de 175.000 personas pudieron informar rápidamente a otros de un riesgo de infección e interrumpir así las cadenas de contagio.\n\nCon la suspensión de las medidas por parte del gobierno federal, también la aplicación SwissCovid deja de estar operativa. La aplicación se ha inactivado y debe ser desinstalada. En caso de que la situación epidemiológica hiciera necesaria su reactivación, nos alegraría contar de nuevo con su apoyo."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Muchas gracias por descargar la aplicación SwissCovid.\n\nCon la utilización de la app SwissCovid ha realizado usted un valioso aporte a la lucha contra la pandemia. Más de 175.000 personas pudieron informar rápidamente a otros de un riesgo de infección e interrumpir así las cadenas de contagio.\n\nCon la suspensión de las medidas por parte del gobierno federal, también la aplicación SwissCovid deja de estar operativa. La aplicación debe ser desinstalada. En caso de que la situación epidemiológica hiciera necesaria su reactivación, nos alegraría contar de nuevo con su apoyo."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Más información"; diff --git a/Translations/fr.lproj/Localizable.strings b/Translations/fr.lproj/Localizable.strings index 77117c748..c0d2436d0 100644 --- a/Translations/fr.lproj/Localizable.strings +++ b/Translations/fr.lproj/Localizable.strings @@ -1942,3 +1942,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19-vac-check.ch/"; "vaccination_impf_check_title" = "Prendre un rendez-vous maintenant"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Information"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "L’application SwissCovid n’est plus en service"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Nous vous remercions d’avoir téléchargé l’application SwissCovid.\n\nEn utilisant cette application, vous avez fourni une contribution essentielle à la lutte contre la pandémie. Plus de 175 000 personnes ont pu rapidement informer d’autres d'un risque d’infection et ainsi interrompre les chaînes de transmission.\n\nParallèlement à la levée des mesures de la Confédération, l’exploitation de l’application SwissCovid a été suspendue. L’application est inactive dès à présent et doit être désinstallée. Si une réactivation s’avérait nécessaire en raison de la situation épidémiologique, nous serions à nouveau heureux de pouvoir compter sur votre soutien."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Nous vous remercions d’avoir téléchargé l’application SwissCovid.\n\nEn utilisant cette application, vous avez fourni une contribution essentielle à la lutte contre la pandémie. Plus de 175 000 personnes ont pu rapidement informer d’autres d'un risque d’infection et ainsi interrompre les chaînes de transmission.\n\nParallèlement à la levée des mesures de la Confédération, l’exploitation de l’application SwissCovid a été suspendue. L’application doit être désinstallée. Si une réactivation s’avérait nécessaire en raison de la situation épidémiologique, nous serions à nouveau heureux de pouvoir compter sur votre soutien."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Informations complémentaires"; diff --git a/Translations/hr.lproj/Localizable.strings b/Translations/hr.lproj/Localizable.strings index 11fe5f454..46a0af052 100644 --- a/Translations/hr.lproj/Localizable.strings +++ b/Translations/hr.lproj/Localizable.strings @@ -1907,3 +1907,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Zakažite sada termin"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Informacije"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "Rad aplikacije SvissCovid je obustavljen"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Hvala vam što ste preuzeli aplikaciju SwissCovid.\n\nKorišćenjem aplikacije SwissCovid dali ste relevantan doprinos u borbi protiv pandemije. Više od 175.000 ljudi bilo je u mogućnosti da brzo informiše druge o riziku od infekcije i tako prekine lance infekcije.\n\nUkidanjem saveznih mera obustavljen je i rad aplikacije SwissCovid. Aplikacija je sada neaktivna i trebalo bi da je deinstalirate. Ukoliko bi reaktivacija bila neophodna zbog epidemiološke situacije, ponovo se radujemo vašoj podršci."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Hvala vam što ste preuzeli aplikaciju SwissCovid.\n\nKorišćenjem aplikacije SwissCovid dali ste relevantan doprinos u borbi protiv pandemije. Više od 175.000 ljudi bilo je u mogućnosti da brzo informiše druge o riziku od infekcije i tako prekine lance infekcije.\n\nUkidanjem saveznih mera obustavljen je i rad aplikacije SwissCovid. Aplikacija je sada neaktivna i trebalo bi da je deinstalirate. Ukoliko bi reaktivacija bila neophodna zbog epidemiološke situacije, ponovo se radujemo vašoj podršci."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Dalje informacije"; diff --git a/Translations/it.lproj/Localizable.strings b/Translations/it.lproj/Localizable.strings index 8a4da32ea..337acbb97 100644 --- a/Translations/it.lproj/Localizable.strings +++ b/Translations/it.lproj/Localizable.strings @@ -1942,3 +1942,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19-vac-check.ch/"; "vaccination_impf_check_title" = "Prendi appuntamento ora"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Informazione"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "Il funzionamento dell'app SwissCovid è stato sospeso"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Grazie per aver scaricato l'app SwissCovid.\n\nUtilizzando l'app SwissCovid hai fornito un contributo rilevante alla lotta alla pandemia. Oltre 175 000 persone hanno potuto informare gli altri rapidamente di un rischio di contagio, interrompendo così le catene di infezione.\n\nCon l'abrogazione dei provvedimenti della Confederazione, anche il funzionamento dell'app SwissCovid è stato sospeso. L'app è inattiva da subito e deve essere disinstallata. Se la situazione epidemiologica dovesse rendere necessaria una riattivazione, saremmo lieti di poter contare nuovamente sul tuo sostegno."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Grazie per aver scaricato l'app SwissCovid.\n\nUtilizzando l'app SwissCovid hai fornito un contributo rilevante alla lotta alla pandemia. Oltre 175 000 persone hanno potuto informare gli altri rapidamente di un rischio di contagio, interrompendo così le catene di infezione.\n\nCon l'abrogazione dei provvedimenti della Confederazione, anche il funzionamento dell'app SwissCovid è stato sospeso. L'app deve essere disinstallata. Se la situazione epidemiologica dovesse rendere necessaria una riattivazione, saremmo lieti di poter contare nuovamente sul tuo sostegno."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Ulteriori informazioni"; diff --git a/Translations/pt.lproj/Localizable.strings b/Translations/pt.lproj/Localizable.strings index 6d9f4ec0e..9cb268ec7 100644 --- a/Translations/pt.lproj/Localizable.strings +++ b/Translations/pt.lproj/Localizable.strings @@ -1909,3 +1909,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Agende a data agora"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Informação"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "A operação do aplicativo SwissCovid cessou"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Agradecemos ter baixado o aplicativo SwissCovid.\n\nCom a utilização do aplicativo você prestou um contributo relevante para o combate à pandemia. Mais de 175.000 pessoas puderam informar outros rapidamente sobre um risco de contágio e interromper as cadeias de infeção.\n\nCom a revogação das medidas da Confederação, a operação do aplicativo SwissCovid também cessou. A partir de agora, o aplicativo é inativo e deve ser desinstalado. Se uma reativação for necessária devido à situação epidemiológica, seríamos muito grato pela sua ajuda."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Agradecemos ter baixado o aplicativo SwissCovid.\n\nCom a utilização do aplicativo você prestou um contributo relevante para o combate à pandemia. Mais de 175.000 pessoas puderam informar outros rapidamente sobre um risco de contágio e interromper as cadeias de infeção.\n\nCom a revogação das medidas da Confederação, a operação do aplicativo SwissCovid também cessou. A partir de agora, o aplicativo é inativo e deve ser desinstalado. Se uma reativação for necessária devido à situação epidemiológica, seríamos muito grato pela sua ajuda."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Mais informações"; diff --git a/Translations/rm.lproj/Localizable.strings b/Translations/rm.lproj/Localizable.strings index 264f37ba3..c6e55f9fd 100644 --- a/Translations/rm.lproj/Localizable.strings +++ b/Translations/rm.lproj/Localizable.strings @@ -1907,3 +1907,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Reservar in termin ussa"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Infurmaziun"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "Il manaschi da l'app SwissCovid è vegnì terminà."; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Grazia fitg che Vus avais chargià giu l'app SwissCovid.\n\nCun duvrar l'app SwissCovid avais Vus prestà ina contribuziun impurtanta en il cumbat cunter la pandemia. Passa 175 000 persunas han pudì infurmar autras persunas davart ina ristga d'infecziun ed han uschia pudì interrumper chadainas d'infecziun.\n\nCun l'aboliziun da las mesiras da la Confederaziun è er vegnì terminà il manaschi da l'app SwissCovid. L'app è per immediat inactiva e duai vegnir deinstallada. Sch'ella stuess puspè vegnir activada causa la situaziun epidemiologica, ans legrassan nus puspè da Voss sustegn."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Grazia fitg che Vus avais chargià giu l'app SwissCovid.\n\nCun duvrar l'app SwissCovid avais Vus prestà ina contribuziun impurtanta en il cumbat cunter la pandemia. Passa 175 000 persunas han pudì infurmar autras persunas davart ina ristga d'infecziun ed han uschia pudì interrumper chadainas d'infecziun.\n\nCun l'aboliziun da las mesiras da la Confederaziun è er vegnì terminà il manaschi da l'app SwissCovid. L'app duai vegnir deinstallada. Sch'ella stuess puspè vegnir activada causa la situaziun epidemiologica, ans legrassan nus puspè da Voss sustegn."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Ulteriuras infurmaziuns"; diff --git a/Translations/sq.lproj/Localizable.strings b/Translations/sq.lproj/Localizable.strings index ed56d8642..360756b03 100644 --- a/Translations/sq.lproj/Localizable.strings +++ b/Translations/sq.lproj/Localizable.strings @@ -1909,3 +1909,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Rezervoni një takim tani"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Informacion"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "Funksionimi i aplikacionit “SwissCovid” u ndërpre"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Faleminderit shumë, që ju e keni shkarkuar aplikacionin “SwissCovid”.\n\nMe përdorimin e aplikacionin “SwissCovid” ju keni dhënë një kontribut të spikatshëm për luftimin e pandemisë. Mbi 175’000 persona mundën të informonin shpejt të tjerët mbi një rrezik infektimi dhe të ndërprisnin me këtë zinxhirin e infektimit.\n\nMe shfuqizimin e masave të federatës u ndërpre edhe funksionimi i aplikacionit “SwissCovid”. Aplikacioni është menjëherë joveprues dhe duhet të deinstalohet. Nëse për shkak të situatës epidemiologjike do të jetë i nevojshëm një riaktivizim, ne do të gëzoheshim përsëri për mbështetjen tuaj."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Faleminderit shumë, që ju e keni shkarkuar aplikacionin “SwissCovid”.\n\nMe përdorimin e aplikacionin “SwissCovid” ju keni dhënë një kontribut të spikatshëm për luftimin e pandemisë. Mbi 175’000 persona mundën të informonin shpejt të tjerët mbi një rrezik infektimi dhe të ndërprisnin me këtë zinxhirin e infektimit.\n\nMe shfuqizimin e masave të federatës u ndërpre edhe funksionimi i aplikacionit “SwissCovid”. Aplikacioni duhet të deinstalohet. Nëse për shkak të situatës epidemiologjike do të jetë i nevojshëm një riaktivizim, ne do të gëzoheshim përsëri për mbështetjen tuaj."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Informacione të tjera"; diff --git a/Translations/sr-Latn-RS.lproj/Localizable.strings b/Translations/sr-Latn-RS.lproj/Localizable.strings index 48b77b28b..dd24236a0 100644 --- a/Translations/sr-Latn-RS.lproj/Localizable.strings +++ b/Translations/sr-Latn-RS.lproj/Localizable.strings @@ -1907,3 +1907,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Zakažite sada termin"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Informacije"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "Rad aplikacije SvissCovid je obustavljen"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "Hvala vam što ste preuzeli aplikaciju SwissCovid.\n\nKorišćenjem aplikacije SwissCovid dali ste relevantan doprinos u borbi protiv pandemije. Više od 175.000 ljudi bilo je u mogućnosti da brzo informiše druge o riziku od infekcije i tako prekine lance infekcije.\n\nUkidanjem saveznih mera obustavljen je i rad aplikacije SwissCovid. Aplikacija je sada neaktivna i trebalo bi da je deinstalirate. Ukoliko bi reaktivacija bila neophodna zbog epidemiološke situacije, ponovo se radujemo vašoj podršci."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "Hvala vam što ste preuzeli aplikaciju SwissCovid.\n\nKorišćenjem aplikacije SwissCovid dali ste relevantan doprinos u borbi protiv pandemije. Više od 175.000 ljudi bilo je u mogućnosti da brzo informiše druge o riziku od infekcije i tako prekine lance infekcije.\n\nUkidanjem saveznih mera obustavljen je i rad aplikacije SwissCovid. Aplikacija je sada neaktivna i trebalo bi da je deinstalirate. Ukoliko bi reaktivacija bila neophodna zbog epidemiološke situacije, ponovo se radujemo vašoj podršci."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Dalje informacije"; diff --git a/Translations/ti.lproj/Localizable.strings b/Translations/ti.lproj/Localizable.strings index 57fece8ea..865fcc359 100644 --- a/Translations/ti.lproj/Localizable.strings +++ b/Translations/ti.lproj/Localizable.strings @@ -1908,3 +1908,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "ሕጂ ቘጸራ ሓዝ"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "ሓበሬታ"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "ኣሰራርሓ እቲ SwissCovid ዚብሃል ኣፕሊኬሽን ተቛሪጹ እዩ።"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "ነቲ ስዊዝኮቪድ ዚብሃል ኣፕሊኬሽን ስለ ዘራገፍኩምልና (ዳውንሎውድ ብምግባርኩም) አመስግነኩም።\n\nነቲ ስዊዝኮቪድ ዚብሃል ኣፕሊኬሽን ተጠቒምካ ነዚ ለብዒ እዚ ንምቅላስ ኣገዳሲ ኣበርክቶ ጌርካ ኢኻ። ልዕሊ 175,000 ዝዀኑ ሰባት ንኻልኦት ሰባት ብዛዕባ እቲ ኼጋጥም ዚኽእል ረኽሲ ቐልጢፎም ኪነግርዎም ንሰንሰለት እቲ ረኽሲ ድማ ኬቋርጽዎ ኽኢሎም እዮም።\n\nእቲ ፈደራላዊ መንግስቲ ዝወሰዶ ስጕምትታት ምስ ተደምሰሰ ኣሰራርሓ እቲ ስዊዝኮቪድ ዚብሃል ኣፕሊኬሽን እውን ተቛረጸ። እቲ ኣፕሊኬሽን ብኡንብኡ ዘይንጡፍ ስለ ዝዀነ ኪቕመጥ ኣለዎ። ብሰንኪ እቲ ለብዒ እንደገና ምላሽ ምሃብ ኣድላዪ እንተ ዀይኑ እንደገና ደገፍካ ኽንረክብ ሕጉሳት ኢና ።"; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "ነቲ ስዊዝኮቪድ ዚብሃል ኣፕሊኬሽን ስለ ዘራገፍኩምልና (ዳውንሎውድ ብምግባርኩም) አመስግነኩም።\n\nነቲ ስዊዝኮቪድ ዚብሃል ኣፕሊኬሽን ተጠቒምካ ነዚ ለብዒ እዚ ንምቅላስ ኣገዳሲ ኣበርክቶ ጌርካ ኢኻ። ልዕሊ 175,000 ዝዀኑ ሰባት ንኻልኦት ሰባት ብዛዕባ እቲ ኼጋጥም ዚኽእል ረኽሲ ቐልጢፎም ኪነግርዎም ንሰንሰለት እቲ ረኽሲ ድማ ኬቋርጽዎ ኽኢሎም እዮም።\n\nእቲ ፈደራላዊ መንግስቲ ዝወሰዶ ስጕምትታት ምስ ተደምሰሰ ኣሰራርሓ እቲ ስዊዝኮቪድ ዚብሃል ኣፕሊኬሽን እውን ተቛረጸ። እቲ ኣፕሊኬሽን ኪገጣጠም ኣለዎ። ብሰንኪ እቲ ለብዒ እንደገና ምላሽ ምሃብ ኣድላዪ እንተ ዀይኑ እንደገና ደገፍካ ኽንረክብ ሕጉሳት ኢና።"; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "ዝያዳ ሓበሬታ"; diff --git a/Translations/tr.lproj/Localizable.strings b/Translations/tr.lproj/Localizable.strings index 64f1465e0..dafb90716 100644 --- a/Translations/tr.lproj/Localizable.strings +++ b/Translations/tr.lproj/Localizable.strings @@ -1908,3 +1908,18 @@ /*URL to ImpfCheck*/ "vaccination_impf_check_url" = "https://covid19.impf-check.ch/"; "vaccination_impf_check_title" = "Hemen randevu alın"; + +/*Über Titel auf Abschaltungs-Screen*/ +"termination_header" = "Bilgilendirme"; + +/*Titel auf Abschaltungs-Screen*/ +"termination_title" = "SwissCovid uygulaması devre dışı bırakıldı"; + +/*Text auf Abschaltungs-Screen*/ +"termination_text" = "SwissCovid uygulamasını yüklediğiniz için çok teşekkür ederiz.\n\nSwissCovid uygulamasını kullanmakla pandemiyle mücadeleye önemli bir katkı sağlamış oldunuz. Sayıları 175’000'i geçen kişi bulaşma riski konusunda diğerlerini hızlı bir şekilde bilgilendirme imkânına sahip oldu ve dolayısıyla enfeksiyon zincirini kesintiye uğratabildi.\n\nFederal resmi makamların önlemleri kaldırmasıyla birlikte SwissCovid uygulaması da devre dışı bırakıldı. Bu uygulama şu andan itibaren aktif durumda değildir ve silinmesi gerekmektedir. Epidemiyolojik durum bunun yeniden aktif hale getirilmesini gerekli kılarsa yine destek sağlamanıza seviniriz."; + +/*Gleicher Text wie auf Abschaltungs-Screen für Nutzer, die die App nicht auf die aktuellste Version aktualisiert haben, mit dem Unterschied, dass hier die App nicht per sofort komplett inaktiv ist*/ +"termination_text_infobox" = "SwissCovid uygulamasını yüklediğiniz için çok teşekkür ederiz.\n\nSwissCovid uygulamasını kullanmakla pandemiyle mücadeleye önemli bir katkı sağlamış oldunuz. Sayıları 175’000'i geçen kişi bulaşma riski konusunda diğerlerini hızlı bir şekilde bilgilendirme imkânına sahip oldu ve dolayısıyla enfeksiyon zincirini kesintiye uğratabildi.\n\nFederal resmi makamların önlemleri kaldırmasıyla birlikte SwissCovid uygulaması da devre dışı bırakıldı. Uygulamanın silinmesi gerekmektedir. Epidemiyolojik durum bunun yeniden aktif hale getirilmesini gerekli kılarsa yine destek sağlamanıza seviniriz."; + +/*Link Text auf Abschaltungs-Screen*/ +"termination_link_title" = "Daha fazla bilgi"; From 1b81be86819129ecd1367dc5f059f1baf3d95843 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Thu, 17 Mar 2022 16:07:22 +0100 Subject: [PATCH 14/28] Config request is already started in every `applicationWillEnterForeground` event (as part of `startForceUpdateCheck()) --- DP3TApp/Logic/AppDelegate.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/DP3TApp/Logic/AppDelegate.swift b/DP3TApp/Logic/AppDelegate.swift index 2cc94b515..a52c7f199 100644 --- a/DP3TApp/Logic/AppDelegate.swift +++ b/DP3TApp/Logic/AppDelegate.swift @@ -137,8 +137,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidBecomeActive(_: UIApplication) { // Start sync after app became active TracingManager.shared.updateStatus(shouldSync: true, completion: nil) - - ConfigManager().startConfigRequest(window: window) } private func willAppearAfterColdstart(_: UIApplication, coldStart: Bool, backgroundTime: TimeInterval) { From f2a5f7cf6cd3f5a93b462d0bda6c64e20543ce74 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Thu, 17 Mar 2022 16:07:47 +0100 Subject: [PATCH 15/28] Ignore local cache when performing config request --- DP3TApp/Logic/Config/ConfigManager.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 371610c81..ae091ce26 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -113,7 +113,8 @@ class ConfigManager: NSObject { } public func loadConfig(backgroundTask: Bool, completion: @escaping (ConfigResponseBody?) -> Void) { - let request = Endpoint.config(appversion: ConfigManager.appVersion, osversion: ConfigManager.osVersion, buildnr: ConfigManager.buildNumber).request() + var request = Endpoint.config(appversion: ConfigManager.appVersion, osversion: ConfigManager.osVersion, buildnr: ConfigManager.buildNumber).request() + request.cachePolicy = .reloadIgnoringLocalCacheData guard Self.shouldLoadConfig(backgroundTask: backgroundTask, url: request.url?.absoluteString, From 879c3c2921dc6f6b06eec968f5fdda84715324b4 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Thu, 17 Mar 2022 16:17:37 +0100 Subject: [PATCH 16/28] Revert "bump version to 2.4.0" This reverts commit 897d9bfa51d74adfb67841087849bed8c00e6806. --- DP3TApp/Supporting Files/Info.plist | 2 +- DP3TAppClip/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DP3TApp/Supporting Files/Info.plist b/DP3TApp/Supporting Files/Info.plist index 49c72670c..440d06873 100644 --- a/DP3TApp/Supporting Files/Info.plist +++ b/DP3TApp/Supporting Files/Info.plist @@ -24,7 +24,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.4.0 + 2.3.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) ENAPIVersion diff --git a/DP3TAppClip/Info.plist b/DP3TAppClip/Info.plist index b69bc5c80..0135be819 100644 --- a/DP3TAppClip/Info.plist +++ b/DP3TAppClip/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.4.0 + 2.3.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS From ab9e8b78177d2f6d5fb47d2c5cdfd50b417a53c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Thu, 17 Mar 2022 16:57:57 +0100 Subject: [PATCH 17/28] Fix crash for unsupported devices --- DP3TApp/Logic/Tracing/TracingManager.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/DP3TApp/Logic/Tracing/TracingManager.swift b/DP3TApp/Logic/Tracing/TracingManager.swift index 0f70423d6..674991ce0 100644 --- a/DP3TApp/Logic/Tracing/TracingManager.swift +++ b/DP3TApp/Logic/Tracing/TracingManager.swift @@ -109,7 +109,7 @@ class TracingManager: NSObject { } func requestTracingPermission(completion: @escaping (Error?) -> Void) { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } DP3TTracing.startTracing { result in switch result { @@ -122,7 +122,7 @@ class TracingManager: NSObject { } func startTracing(callback: ((TracingEnableResult) -> Void)? = nil) { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } if UserStorage.shared.hasCompletedOnboarding, ConfigManager.allowTracing { DP3TTracing.startTracing(completionHandler: { result in switch result { @@ -151,13 +151,13 @@ class TracingManager: NSObject { } func endTracing() { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } DP3TTracing.stopTracing() localPush.removeSyncWarningTriggers() } func resetSDK() { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } // completely reset SDK DP3TTracing.reset() @@ -168,7 +168,7 @@ class TracingManager: NSObject { } func deletePositiveTest() { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } UIStateManager.shared.blockUpdate { // reset end isolation question date and onset date ReportingManager.shared.endIsolationQuestionDate = nil @@ -193,7 +193,7 @@ class TracingManager: NSObject { } func deleteReports() { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } // delete all visible messages DP3TTracing.resetExposureDays() @@ -206,7 +206,7 @@ class TracingManager: NSObject { } func userHasCompletedOnboarding() { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } if ConfigManager.allowTracing, UserStorage.shared.tracingSettingEnabled { DP3TTracing.startTracing { result in switch result { @@ -252,7 +252,7 @@ class TracingManager: NSObject { } func setBackgroundRefreshEnabled(_ enabled: Bool) { - guard #available(iOS 12.5, *) else { return } + guard #available(iOS 12.5, *), isSupported else { return } DP3TTracing.setBackgroundTasksEnabled(enabled) } From 771da3e7e22cbf938e494ace7d11098124fba3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Thu, 17 Mar 2022 17:22:57 +0100 Subject: [PATCH 18/28] Also show deactivated screen if OS is not supported --- DP3TApp/Logic/AppDelegate.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DP3TApp/Logic/AppDelegate.swift b/DP3TApp/Logic/AppDelegate.swift index a52c7f199..7eb2928a6 100644 --- a/DP3TApp/Logic/AppDelegate.swift +++ b/DP3TApp/Logic/AppDelegate.swift @@ -144,6 +144,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Nothing to do here if device is not supported guard TracingManager.shared.isSupported else { + startForceUpdateCheck() return } From 7bddbd4121a804f3434e2389a649dcba851328c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Ju=CC=88ni?= Date: Thu, 17 Mar 2022 19:38:09 +0100 Subject: [PATCH 19/28] fix reloading of deactivation screen on every didBecomeActive --- DP3TApp/Logic/Config/ConfigManager.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index ae091ce26..21fa27bf8 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -166,6 +166,9 @@ class ConfigManager: NSObject { if let config = config { self.presentDeactivationIfNeeded(config: config, window: window) self.presentAlertIfNeeded(config: config, window: window) + } else { + //still show deactivationScreen if no internet + self.presentDeactivationIfNeeded(config: ConfigManager.currentConfig) } } } @@ -216,17 +219,21 @@ class ConfigManager: NSObject { } } - public func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow?) { + public func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow? = nil) { guard let config = config else { return } if config.deactivate { let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) if let window = window { - window.rootViewController = vc + if (window.rootViewController as? NSNavigationController)?.visibleViewController as? NSDeactivatedInfoViewController == nil { + window.rootViewController = vc + } } else { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - appDelegate.window?.rootViewController? = vc + if (appDelegate.window?.rootViewController as? NSNavigationController)?.visibleViewController as? NSDeactivatedInfoViewController == nil { + appDelegate.window?.rootViewController? = vc + } } if TracingManager.shared.isActivated { From ad815704c2507ecbd61b2f65f174a55c967830f9 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Thu, 17 Mar 2022 23:14:07 +0100 Subject: [PATCH 20/28] Remove comment --- DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift index 018f36a78..845f49820 100644 --- a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift +++ b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift @@ -58,7 +58,7 @@ class NSDeactivatedInfoView: NSSimpleModuleBaseView { } private func moreInfoTouched() { - if let url = ConfigManager.currentConfig?.deactivationMessage?.value?.url { // URL(string: ConfigManager.currentConfig?.deactivationMessage?.value?.url ?? "https://www.google.ch") { + if let url = ConfigManager.currentConfig?.deactivationMessage?.value?.url { UIApplication.shared.open(url, options: [:], completionHandler: nil) } else { print("URL is null") From a20513439a1494bff50276bb72f257fd7710b06c Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Fri, 18 Mar 2022 16:05:37 +0100 Subject: [PATCH 21/28] use cached config for daktivation screen --- DP3TApp/Logic/AppDelegate.swift | 6 +-- .../Logic/Config/ConfigLoadOperation.swift | 8 +-- DP3TApp/Logic/Config/ConfigManager.swift | 53 +++++++++++-------- .../Networking/Base/URLSession+pinning.swift | 9 ++-- DP3TApp/Logic/Tracing/TracingManager.swift | 2 +- DP3TApp/Logic/User/UserStorage.swift | 3 ++ .../Deactivated/NSDeactivatedInfoView.swift | 2 +- 7 files changed, 46 insertions(+), 37 deletions(-) diff --git a/DP3TApp/Logic/AppDelegate.swift b/DP3TApp/Logic/AppDelegate.swift index 7eb2928a6..9ee3b5d1a 100644 --- a/DP3TApp/Logic/AppDelegate.swift +++ b/DP3TApp/Logic/AppDelegate.swift @@ -114,9 +114,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.makeKeyAndVisible() - let deactivated = UserStorage.shared.appDeactivated - - guard !deactivated else { return } + guard !(ConfigManager.currentConfig?.deactivate ?? false) else { return } if UserStorage.shared.appClipCheckinUrl() != nil { let checkinOnboardingVC = NSCheckinOnboardingViewController() @@ -167,6 +165,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ = jumpToMessageIfRequired(onlyFirst: false) } + ConfigManager.presentDeactivationIfNeeded(config: ConfigManager.currentConfig, window: window) + startForceUpdateCheck() FakePublishManager.shared.runTask() diff --git a/DP3TApp/Logic/Config/ConfigLoadOperation.swift b/DP3TApp/Logic/Config/ConfigLoadOperation.swift index 4ea140677..1eda7f723 100644 --- a/DP3TApp/Logic/Config/ConfigLoadOperation.swift +++ b/DP3TApp/Logic/Config/ConfigLoadOperation.swift @@ -41,17 +41,17 @@ class ConfigLoadOperation: Operation { appDelegate.window?.rootViewController? = vc } + TracingManager.shared.setBackgroundRefreshEnabled(false) + if TracingManager.shared.isActivated { TracingManager.shared.endTracing() + UserStorage.shared.tracingWasActivatedBeforeDeaktivation = true } - TracingManager.shared.setBackgroundRefreshEnabled(false) UBPushManager.shared.setActive(false) CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) - if !UserStorage.shared.appDeactivated { - UserStorage.shared.appDeactivated = true - } + UserStorage.shared.appDeactivated = true } else { self.cancel() diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 21fa27bf8..b4bf0f273 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -164,11 +164,8 @@ class ConfigManager: NSObject { loadConfig(backgroundTask: false) { config in // self must be strong if let config = config { - self.presentDeactivationIfNeeded(config: config, window: window) + Self.presentDeactivationIfNeeded(config: config, window: window) self.presentAlertIfNeeded(config: config, window: window) - } else { - //still show deactivationScreen if no internet - self.presentDeactivationIfNeeded(config: ConfigManager.currentConfig) } } } @@ -219,45 +216,55 @@ class ConfigManager: NSObject { } } - public func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow? = nil) { + public static func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow? = nil) { guard let config = config else { return } if config.deactivate { + if (window?.rootViewController as? UINavigationController)?.visibleViewController is NSDeactivatedInfoViewController { + // The NSDeactivatedInfoViewController is already visible + return + } + let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) if let window = window { - if (window.rootViewController as? NSNavigationController)?.visibleViewController as? NSDeactivatedInfoViewController == nil { - window.rootViewController = vc - } - } else { - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - if (appDelegate.window?.rootViewController as? NSNavigationController)?.visibleViewController as? NSDeactivatedInfoViewController == nil { - appDelegate.window?.rootViewController? = vc - } + window.rootViewController = vc } + TracingManager.shared.setBackgroundRefreshEnabled(false) + if TracingManager.shared.isActivated { + UserStorage.shared.tracingWasActivatedBeforeDeaktivation = true TracingManager.shared.endTracing() } - TracingManager.shared.setBackgroundRefreshEnabled(false) UBPushManager.shared.setActive(false) CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) - if !UserStorage.shared.appDeactivated { - UserStorage.shared.appDeactivated = true - } + + UserStorage.shared.appDeactivated = true + } else if !config.deactivate, UserStorage.shared.appDeactivated { - if TracingManager.shared.isAuthorized { + if TracingManager.shared.isAuthorized, UserStorage.shared.tracingWasActivatedBeforeDeaktivation { TracingManager.shared.startTracing() } + TracingManager.shared.setBackgroundRefreshEnabled(true) - UserStorage.shared.appDeactivated = false - if !UserStorage.shared.hasCompletedOnboarding { - let onboardingViewController = NSOnboardingViewController() - onboardingViewController.modalPresentationStyle = .fullScreen - window?.rootViewController?.present(onboardingViewController, animated: false) + if TracingManager.shared.isSupported { + window?.rootViewController = (UIApplication.shared.delegate as? AppDelegate)?.navigationController + + if !UserStorage.shared.hasCompletedOnboarding { + let onboardingViewController = NSOnboardingViewController() + onboardingViewController.modalPresentationStyle = .fullScreen + window?.rootViewController?.present(onboardingViewController, animated: false) + } + + } else { + window?.rootViewController = NSUnsupportedOSViewController() } + + UserStorage.shared.appDeactivated = false + UserStorage.shared.tracingWasActivatedBeforeDeaktivation = true } } } diff --git a/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift b/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift index 67541b72a..ddb6d0882 100644 --- a/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift +++ b/DP3TApp/Logic/Networking/Base/URLSession+pinning.swift @@ -95,15 +95,14 @@ class CertificateEvaluator: NSObject, URLSessionDelegate { "www.pt-a.bfs.admin.ch", "www.pt-t.bfs.admin.ch", "www.pt.bfs.admin.ch"] - + if let QuovadisRootCA = bundle.getCertificate(with: "QuoVadis-Root-CA-2-G3", fileExtension: "cer") { for host in hosts { let evaluator = UBPinnedCertificatesTrustEvaluator(certificates: [QuovadisRootCA], - acceptSelfSignedCertificates: true, - performDefaultValidation: false, - validateHost: true) + acceptSelfSignedCertificates: true, + performDefaultValidation: false, + validateHost: true) evaluators[host] = evaluator - } } else { assertionFailure("Could not load certificate for pinned host") diff --git a/DP3TApp/Logic/Tracing/TracingManager.swift b/DP3TApp/Logic/Tracing/TracingManager.swift index 674991ce0..5cec7ea5b 100644 --- a/DP3TApp/Logic/Tracing/TracingManager.swift +++ b/DP3TApp/Logic/Tracing/TracingManager.swift @@ -277,7 +277,7 @@ extension TracingManager: DP3TTracingDelegate { isAuthorized = (state.trackingState != .inactive(error: .authorizationUnknown) && state.trackingState != .inactive(error: .permissionError)) - let needsPush = !isAuthorized && CrowdNotifier.hasCheckins() && !UserStorage.shared.appDeactivated + let needsPush = !isAuthorized && CrowdNotifier.hasCheckins() && !(ConfigManager.currentConfig?.deactivate ?? false) UBPushManager.shared.setActive(needsPush) // update tracing error states if needed diff --git a/DP3TApp/Logic/User/UserStorage.swift b/DP3TApp/Logic/User/UserStorage.swift index 8f75cbaa2..cc4c2c07d 100644 --- a/DP3TApp/Logic/User/UserStorage.swift +++ b/DP3TApp/Logic/User/UserStorage.swift @@ -80,6 +80,9 @@ class UserStorage { @UBUserDefault(key: "appDeactivated", defaultValue: false) var appDeactivated: Bool + @UBUserDefault(key: "tracingWasActivatedBeforeDeaktivation", defaultValue: false) + var tracingWasActivatedBeforeDeaktivation: Bool + // method to get AppClip url in Main App public func appClipCheckinUrl() -> String? { let bi = (Bundle.main.bundleIdentifier ?? "") diff --git a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift index 845f49820..513ab8c59 100644 --- a/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift +++ b/DP3TApp/Screens/Deactivated/NSDeactivatedInfoView.swift @@ -58,7 +58,7 @@ class NSDeactivatedInfoView: NSSimpleModuleBaseView { } private func moreInfoTouched() { - if let url = ConfigManager.currentConfig?.deactivationMessage?.value?.url { + if let url = ConfigManager.currentConfig?.deactivationMessage?.value?.url { UIApplication.shared.open(url, options: [:], completionHandler: nil) } else { print("URL is null") From de3f8321f101ab33f16bda17b10ae7ef95f196b0 Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Sat, 19 Mar 2022 10:09:46 +0100 Subject: [PATCH 22/28] removes appclip from target --- DP3TApp.xcodeproj/project.pbxproj | 55 ------------------------------- 1 file changed, 55 deletions(-) diff --git a/DP3TApp.xcodeproj/project.pbxproj b/DP3TApp.xcodeproj/project.pbxproj index b05f6e38c..0f5850731 100644 --- a/DP3TApp.xcodeproj/project.pbxproj +++ b/DP3TApp.xcodeproj/project.pbxproj @@ -223,7 +223,6 @@ 6E5B6979265F6A0500EB0C56 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6E5B6978265F6A0500EB0C56 /* Assets.xcassets */; }; 6E5B697C265F6A0500EB0C56 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6E5B697B265F6A0500EB0C56 /* Preview Assets.xcassets */; }; 6E5B697F265F6A0500EB0C56 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6E5B697D265F6A0500EB0C56 /* LaunchScreen.storyboard */; }; - 6E5B6984265F6A0500EB0C56 /* DP3TAppClip.app in Embed App Clips */ = {isa = PBXBuildFile; fileRef = 6E5B6970265F6A0400EB0C56 /* DP3TAppClip.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6E5B698F265F6C6E00EB0C56 /* CrowdNotifierBaseSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6E5B698E265F6C6E00EB0C56 /* CrowdNotifierBaseSDK */; }; 6E5B6991265F6C8A00EB0C56 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 6E5B6990265F6C8A00EB0C56 /* SnapKit */; }; 6E5B6993265F6C8A00EB0C56 /* Clibsodium in Frameworks */ = {isa = PBXBuildFile; productRef = 6E5B6992265F6C8A00EB0C56 /* Clibsodium */; }; @@ -473,7 +472,6 @@ DC746D5A2451DF78009426B1 /* NSHeaderActiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC746D592451DF78009426B1 /* NSHeaderActiveView.swift */; }; DC85DEFD263ADEDA002DF191 /* NSCheckInDetailCheckedOutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC85DEFC263ADEDA002DF191 /* NSCheckInDetailCheckedOutView.swift */; }; DC85DEFE263ADEDA002DF191 /* NSCheckInDetailCheckedOutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC85DEFC263ADEDA002DF191 /* NSCheckInDetailCheckedOutView.swift */; }; - DC8CAEED266666CB002D78EF /* DP3TAppClip.app in Embed App Clips */ = {isa = PBXBuildFile; fileRef = 6E5B6970265F6A0400EB0C56 /* DP3TAppClip.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; DC8CAEF226677BC7002D78EF /* UIAlertController+CheckoutAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC8CAEF126677BC6002D78EF /* UIAlertController+CheckoutAlert.swift */; }; DC8CAEF326677BC7002D78EF /* UIAlertController+CheckoutAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC8CAEF126677BC6002D78EF /* UIAlertController+CheckoutAlert.swift */; }; DC9A81AB242CE5E000AD1548 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC9A81A1242CE5E000AD1548 /* Localizable.strings */; }; @@ -674,13 +672,6 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 6E5B6982265F6A0500EB0C56 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8E81CC8D241FCC7D006F2437 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 6E5B696F265F6A0400EB0C56; - remoteInfo = DP3TAppClip; - }; 8E81CCAC241FCC7F006F2437 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8E81CC8D241FCC7D006F2437 /* Project object */; @@ -688,40 +679,8 @@ remoteGlobalIDString = 8E81CC94241FCC7D006F2437; remoteInfo = IVC; }; - DC8CAEEE266666CB002D78EF /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8E81CC8D241FCC7D006F2437 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 6E5B696F265F6A0400EB0C56; - remoteInfo = DP3TAppClip; - }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 6E5B6985265F6A0500EB0C56 /* Embed App Clips */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(CONTENTS_FOLDER_PATH)/AppClips"; - dstSubfolderSpec = 16; - files = ( - 6E5B6984265F6A0500EB0C56 /* DP3TAppClip.app in Embed App Clips */, - ); - name = "Embed App Clips"; - runOnlyForDeploymentPostprocessing = 0; - }; - DC8CAEF0266666CB002D78EF /* Embed App Clips */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(CONTENTS_FOLDER_PATH)/AppClips"; - dstSubfolderSpec = 16; - files = ( - DC8CAEED266666CB002D78EF /* DP3TAppClip.app in Embed App Clips */, - ); - name = "Embed App Clips"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 2411CA9E2462ADBE002FB5A9 /* Entitlements.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Entitlements.entitlements; sourceTree = ""; }; 242D21D0245C3853005DAEA8 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; @@ -2210,12 +2169,10 @@ 242D2253245C4BD8005DAEA8 /* Frameworks */, 242D2256245C4BD8005DAEA8 /* Resources */, 242D226B245C4BD8005DAEA8 /* ShellScript */, - DC8CAEF0266666CB002D78EF /* Embed App Clips */, ); buildRules = ( ); dependencies = ( - DC8CAEEF266666CB002D78EF /* PBXTargetDependency */, ); name = DP3TAppCalibration; packageProductDependencies = ( @@ -2265,12 +2222,10 @@ 8E81CC92241FCC7D006F2437 /* Frameworks */, 8E81CC93241FCC7D006F2437 /* Resources */, 240879652440504500001462 /* Run Script */, - 6E5B6985265F6A0500EB0C56 /* Embed App Clips */, ); buildRules = ( ); dependencies = ( - 6E5B6983265F6A0500EB0C56 /* PBXTargetDependency */, ); name = DP3TApp; packageProductDependencies = ( @@ -3190,21 +3145,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 6E5B6983265F6A0500EB0C56 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 6E5B696F265F6A0400EB0C56 /* DP3TAppClip */; - targetProxy = 6E5B6982265F6A0500EB0C56 /* PBXContainerItemProxy */; - }; 8E81CCAD241FCC7F006F2437 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8E81CC94241FCC7D006F2437 /* DP3TApp */; targetProxy = 8E81CCAC241FCC7F006F2437 /* PBXContainerItemProxy */; }; - DC8CAEEF266666CB002D78EF /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 6E5B696F265F6A0400EB0C56 /* DP3TAppClip */; - targetProxy = DC8CAEEE266666CB002D78EF /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ From 99d6f86030613e90190e393ba7dffbb7ea562163 Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Sat, 19 Mar 2022 10:13:59 +0100 Subject: [PATCH 23/28] bump version to 2.4.0 --- DP3TApp/Supporting Files/Info.plist | 2 +- DP3TAppClip/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DP3TApp/Supporting Files/Info.plist b/DP3TApp/Supporting Files/Info.plist index 440d06873..49c72670c 100644 --- a/DP3TApp/Supporting Files/Info.plist +++ b/DP3TApp/Supporting Files/Info.plist @@ -24,7 +24,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.3.2 + 2.4.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) ENAPIVersion diff --git a/DP3TAppClip/Info.plist b/DP3TAppClip/Info.plist index 0135be819..b69bc5c80 100644 --- a/DP3TAppClip/Info.plist +++ b/DP3TAppClip/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.3.2 + 2.4.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS From 12f706d676dd2f5bfdf6a90213c704ef97ee20cb Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Sun, 20 Mar 2022 21:11:17 +0100 Subject: [PATCH 24/28] only switch to main thread if not already on main thread --- DP3TApp/Logic/Config/ConfigLoadOperation.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/DP3TApp/Logic/Config/ConfigLoadOperation.swift b/DP3TApp/Logic/Config/ConfigLoadOperation.swift index 1eda7f723..690b75785 100644 --- a/DP3TApp/Logic/Config/ConfigLoadOperation.swift +++ b/DP3TApp/Logic/Config/ConfigLoadOperation.swift @@ -34,11 +34,16 @@ class ConfigLoadOperation: Operation { ConfigLoadOperation.presentedConfigForVersion = ConfigManager.appVersion } } else if let c = config, c.deactivate { - DispatchQueue.main.sync { + if Thread.isMainThread { let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } appDelegate.window?.rootViewController? = vc + } else { + DispatchQueue.main.async { + let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController()) + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + appDelegate.window?.rootViewController? = vc + } } TracingManager.shared.setBackgroundRefreshEnabled(false) From b24421ba244eadee93098ddd0f1d428ba498d2aa Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Mon, 21 Mar 2022 08:11:22 +0100 Subject: [PATCH 25/28] cancel all pending and delivered notifications on deactivation --- DP3TApp/Logic/Config/ConfigLoadOperation.swift | 2 ++ DP3TApp/Logic/Config/ConfigManager.swift | 2 ++ DP3TApp/Logic/NSLocalPush.swift | 6 ++++++ DP3TApp/Logic/Tracing/DatabaseSyncer.swift | 4 +++- DP3TApp/Logic/Tracing/TracingManager.swift | 6 +++--- DP3TAppTests/MockNotificationCenter.swift | 4 ++++ 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/DP3TApp/Logic/Config/ConfigLoadOperation.swift b/DP3TApp/Logic/Config/ConfigLoadOperation.swift index 690b75785..f29be98fc 100644 --- a/DP3TApp/Logic/Config/ConfigLoadOperation.swift +++ b/DP3TApp/Logic/Config/ConfigLoadOperation.swift @@ -53,6 +53,8 @@ class ConfigLoadOperation: Operation { UserStorage.shared.tracingWasActivatedBeforeDeaktivation = true } + NSLocalPush.shared.cancelAllPendingAndDeliveredNotifications() + UBPushManager.shared.setActive(false) CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0) diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index b4bf0f273..62b03ff1b 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -220,6 +220,8 @@ class ConfigManager: NSObject { guard let config = config else { return } if config.deactivate { + NSLocalPush.shared.cancelAllPendingAndDeliveredNotifications() + if (window?.rootViewController as? UINavigationController)?.visibleViewController is NSDeactivatedInfoViewController { // The NSDeactivatedInfoViewController is already visible return diff --git a/DP3TApp/Logic/NSLocalPush.swift b/DP3TApp/Logic/NSLocalPush.swift index b09b1587b..1c2fc9fa5 100644 --- a/DP3TApp/Logic/NSLocalPush.swift +++ b/DP3TApp/Logic/NSLocalPush.swift @@ -18,6 +18,7 @@ protocol UserNotificationCenter { func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)?) func removeDeliveredNotifications(withIdentifiers identifiers: [String]) func removeAllDeliveredNotifications() + func removeAllPendingNotificationRequests() func removePendingNotificationRequests(withIdentifiers identifiers: [String]) func setNotificationCategories(_ categories: Set) } @@ -140,6 +141,11 @@ class NSLocalPush: NSObject, LocalPushProtocol { center.removePendingNotificationRequests(withIdentifiers: allIdentifier) } + func cancelAllPendingAndDeliveredNotifications() { + center.removeAllDeliveredNotifications() + center.removeAllPendingNotificationRequests() + } + var now: Date { .init() } diff --git a/DP3TApp/Logic/Tracing/DatabaseSyncer.swift b/DP3TApp/Logic/Tracing/DatabaseSyncer.swift index 4d8ed36ff..e6aeb8f54 100644 --- a/DP3TApp/Logic/Tracing/DatabaseSyncer.swift +++ b/DP3TApp/Logic/Tracing/DatabaseSyncer.swift @@ -24,8 +24,10 @@ class DatabaseSyncer { private var databaseSyncInterval: TimeInterval = 10 func syncDatabaseIfNeeded(completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) { + let deactivated = (ConfigManager.currentConfig?.deactivate ?? false) guard !databaseIsSyncing, - UserStorage.shared.hasCompletedOnboarding else { + UserStorage.shared.hasCompletedOnboarding, + !deactivated else { completionHandler?(.noData) return } diff --git a/DP3TApp/Logic/Tracing/TracingManager.swift b/DP3TApp/Logic/Tracing/TracingManager.swift index 5cec7ea5b..52e6ab5a5 100644 --- a/DP3TApp/Logic/Tracing/TracingManager.swift +++ b/DP3TApp/Logic/Tracing/TracingManager.swift @@ -109,7 +109,7 @@ class TracingManager: NSObject { } func requestTracingPermission(completion: @escaping (Error?) -> Void) { - guard #available(iOS 12.5, *), isSupported else { return } + guard #available(iOS 12.5, *), isSupported, ConfigManager.allowTracing else { return } DP3TTracing.startTracing { result in switch result { @@ -122,7 +122,7 @@ class TracingManager: NSObject { } func startTracing(callback: ((TracingEnableResult) -> Void)? = nil) { - guard #available(iOS 12.5, *), isSupported else { return } + guard #available(iOS 12.5, *), isSupported, ConfigManager.allowTracing else { return } if UserStorage.shared.hasCompletedOnboarding, ConfigManager.allowTracing { DP3TTracing.startTracing(completionHandler: { result in switch result { @@ -206,7 +206,7 @@ class TracingManager: NSObject { } func userHasCompletedOnboarding() { - guard #available(iOS 12.5, *), isSupported else { return } + guard #available(iOS 12.5, *), isSupported, ConfigManager.allowTracing else { return } if ConfigManager.allowTracing, UserStorage.shared.tracingSettingEnabled { DP3TTracing.startTracing { result in switch result { diff --git a/DP3TAppTests/MockNotificationCenter.swift b/DP3TAppTests/MockNotificationCenter.swift index 432f567f2..f4214fec5 100644 --- a/DP3TAppTests/MockNotificationCenter.swift +++ b/DP3TAppTests/MockNotificationCenter.swift @@ -29,6 +29,10 @@ class MockNotificationCenter: UserNotificationCenter { requests.removeAll() } + func removeAllPendingNotificationRequests() { + requests.removeAll() + } + func removePendingNotificationRequests(withIdentifiers identifiers: [String]) { requests.removeAll { req -> Bool in identifiers.contains(req.identifier) From 6f75b6f0b717acf16940739b30b533a61f101b7f Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Mon, 21 Mar 2022 08:15:47 +0100 Subject: [PATCH 26/28] guard call to resetBackgroundTaskWarningTriggers --- DP3TApp/Logic/NSLocalPush.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DP3TApp/Logic/NSLocalPush.swift b/DP3TApp/Logic/NSLocalPush.swift index 1c2fc9fa5..662242897 100644 --- a/DP3TApp/Logic/NSLocalPush.swift +++ b/DP3TApp/Logic/NSLocalPush.swift @@ -246,6 +246,8 @@ class NSLocalPush: NSObject, LocalPushProtocol { // This method gets called everytime we get executed in the backgrund or if the app was launched manually func resetBackgroundTaskWarningTriggers() { + // Only schedule warnings if app is not deactivated + guard (ConfigManager.currentConfig?.deactivate ?? false) == false else { return } // Adding a request with the same identifier again automatically cancels an existing request with that identifier, if present scheduleSyncWarningNotification(delay: timeInterval1, identifier: Identifiers.syncWarning1.rawValue) scheduleSyncWarningNotification(delay: timeInterval2, identifier: Identifiers.syncWarning2.rawValue) From aa0c6b65af4e06370708e92b16cee0575dd7b515 Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Mon, 21 Mar 2022 08:32:44 +0100 Subject: [PATCH 27/28] moves config checks of resetBackgroundTaskWarningTriggers into call site (this makes testing easier) --- DP3TApp/Logic/AppDelegate.swift | 5 ++++- .../Logic/CheckIn/CheckInManager/CheckInManager.swift | 10 ++++++++-- DP3TApp/Logic/NSLocalPush.swift | 2 -- DP3TApp/Logic/Push/NSPushHandler.swift | 5 ++++- DP3TApp/Logic/Tracing/DatabaseSyncer.swift | 5 ++++- DP3TApp/Logic/Tracing/TracingManager.swift | 10 ++++++++-- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/DP3TApp/Logic/AppDelegate.swift b/DP3TApp/Logic/AppDelegate.swift index 9ee3b5d1a..8df541f9d 100644 --- a/DP3TApp/Logic/AppDelegate.swift +++ b/DP3TApp/Logic/AppDelegate.swift @@ -45,7 +45,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // defer window initialization if app was launched in // background because of location change if shouldSetupWindow(application: application, launchOptions: launchOptions) { - NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + // Only schedule warnings if app is not deactivated + if ConfigManager.currentConfig?.deactivate != true { + NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + } setupWindow() willAppearAfterColdstart(application, coldStart: true, backgroundTime: 0) } diff --git a/DP3TApp/Logic/CheckIn/CheckInManager/CheckInManager.swift b/DP3TApp/Logic/CheckIn/CheckInManager/CheckInManager.swift index fe21aa4f5..13e4087f2 100644 --- a/DP3TApp/Logic/CheckIn/CheckInManager/CheckInManager.swift +++ b/DP3TApp/Logic/CheckIn/CheckInManager/CheckInManager.swift @@ -78,7 +78,10 @@ class CheckInManager { switch result { case let .success(id): hasCheckedOutOnce = true - NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + // Only schedule warnings if app is not deactivated + if ConfigManager.currentConfig?.deactivate != true { + NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + } cc.identifier = id saveAdditionalInfo(checkIn: cc) case .failure: @@ -122,7 +125,10 @@ class CheckInManager { switch result { case let .success(id): hasCheckedOutOnce = true - NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + // Only schedule warnings if app is not deactivated + if ConfigManager.currentConfig?.deactivate != true { + NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + } var intervalCheckIn = CheckIn(identifier: id, qrCode: checkIn.qrCode, checkInTime: arrivalTime, venue: checkIn.venue) intervalCheckIn.checkOutTime = departureTime saveAdditionalInfo(checkIn: intervalCheckIn) diff --git a/DP3TApp/Logic/NSLocalPush.swift b/DP3TApp/Logic/NSLocalPush.swift index 662242897..1c2fc9fa5 100644 --- a/DP3TApp/Logic/NSLocalPush.swift +++ b/DP3TApp/Logic/NSLocalPush.swift @@ -246,8 +246,6 @@ class NSLocalPush: NSObject, LocalPushProtocol { // This method gets called everytime we get executed in the backgrund or if the app was launched manually func resetBackgroundTaskWarningTriggers() { - // Only schedule warnings if app is not deactivated - guard (ConfigManager.currentConfig?.deactivate ?? false) == false else { return } // Adding a request with the same identifier again automatically cancels an existing request with that identifier, if present scheduleSyncWarningNotification(delay: timeInterval1, identifier: Identifiers.syncWarning1.rawValue) scheduleSyncWarningNotification(delay: timeInterval2, identifier: Identifiers.syncWarning2.rawValue) diff --git a/DP3TApp/Logic/Push/NSPushHandler.swift b/DP3TApp/Logic/Push/NSPushHandler.swift index 22dab576e..4498495cc 100644 --- a/DP3TApp/Logic/Push/NSPushHandler.swift +++ b/DP3TApp/Logic/Push/NSPushHandler.swift @@ -46,7 +46,10 @@ class NSPushHandler: UBPushHandler { } // data are updated -> reschedule background task warning triggers - NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + // Only schedule warnings if app is not deactivated + if ConfigManager.currentConfig?.deactivate != true { + NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + } } if self.backgroundTask != .invalid { diff --git a/DP3TApp/Logic/Tracing/DatabaseSyncer.swift b/DP3TApp/Logic/Tracing/DatabaseSyncer.swift index e6aeb8f54..c909730f7 100644 --- a/DP3TApp/Logic/Tracing/DatabaseSyncer.swift +++ b/DP3TApp/Logic/Tracing/DatabaseSyncer.swift @@ -131,7 +131,10 @@ class DatabaseSyncer { UIStateManager.shared.syncError = nil } - NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + // Only schedule warnings if app is not deactivated + if ConfigManager.currentConfig?.deactivate != true { + NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + } // reload status, user could have been exposed TracingManager.shared.updateStatus(completion: nil) diff --git a/DP3TApp/Logic/Tracing/TracingManager.swift b/DP3TApp/Logic/Tracing/TracingManager.swift index 52e6ab5a5..e78f8cb0a 100644 --- a/DP3TApp/Logic/Tracing/TracingManager.swift +++ b/DP3TApp/Logic/Tracing/TracingManager.swift @@ -298,7 +298,10 @@ extension TracingManager: DP3TBackgroundHandler { #endif // wait another 2 days befor warning - localPush.resetBackgroundTaskWarningTriggers() + // Only schedule warnings if app is not deactivated + if ConfigManager.currentConfig?.deactivate != true { + localPush.resetBackgroundTaskWarningTriggers() + } let queue = OperationQueue() @@ -336,7 +339,10 @@ extension TracingManager: DP3TBackgroundHandler { } // data are updated -> reschedule background task warning triggers - NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + // Only schedule warnings if app is not deactivated + if ConfigManager.currentConfig?.deactivate != true { + NSLocalPush.shared.resetBackgroundTaskWarningTriggers() + } group.leave() } From b030707020147e78e84a13bf746cd01a3a86baad Mon Sep 17 00:00:00 2001 From: Stefan Mitterrutzner Date: Mon, 21 Mar 2022 08:36:53 +0100 Subject: [PATCH 28/28] fixes unit tests --- DP3TApp/Logic/Config/ConfigManager.swift | 7 ++++--- DP3TAppTests/ConfigManagerTests.swift | 17 +++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/DP3TApp/Logic/Config/ConfigManager.swift b/DP3TApp/Logic/Config/ConfigManager.swift index 62b03ff1b..8bd4f0dce 100644 --- a/DP3TApp/Logic/Config/ConfigManager.swift +++ b/DP3TApp/Logic/Config/ConfigManager.swift @@ -80,8 +80,8 @@ class ConfigManager: NSObject { } } - static func shouldLoadConfig(backgroundTask: Bool, url: String?, lastConfigUrl: String?, lastConfigLoad: Date?) -> Bool { - if ConfigManager.currentConfig?.deactivate ?? false { + static func shouldLoadConfig(backgroundTask: Bool, url: String?, lastConfigUrl: String?, lastConfigLoad: Date?, deactivate: Bool?) -> Bool { + if deactivate ?? false { return true } // if the config url was changed (by OS version or app version changing) load config in anycase @@ -119,7 +119,8 @@ class ConfigManager: NSObject { guard Self.shouldLoadConfig(backgroundTask: backgroundTask, url: request.url?.absoluteString, lastConfigUrl: Self.lastConfigUrl, - lastConfigLoad: Self.lastConfigLoad) else { + lastConfigLoad: Self.lastConfigLoad, + deactivate: ConfigManager.currentConfig?.deactivate) else { Logger.log("Skipping config load request and returning from cache", appState: true) completion(Self.currentConfig) return diff --git a/DP3TAppTests/ConfigManagerTests.swift b/DP3TAppTests/ConfigManagerTests.swift index a3aff8b27..46c62b409 100644 --- a/DP3TAppTests/ConfigManagerTests.swift +++ b/DP3TAppTests/ConfigManagerTests.swift @@ -14,29 +14,34 @@ import XCTest class ConfigManagerTests: XCTestCase { func testLoadingAfterTimeForeground() { let lastLoad = Date(timeIntervalSinceNow: -ConfigManager.configForegroundValidityInterval) - XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: false, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad)) + XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: false, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad, deactivate: false)) } func testDontLoadAfterTimeForeground() { let lastLoad = Date(timeIntervalSinceNow: -ConfigManager.configForegroundValidityInterval + 1) - XCTAssertFalse(ConfigManager.shouldLoadConfig(backgroundTask: false, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad)) + XCTAssertFalse(ConfigManager.shouldLoadConfig(backgroundTask: false, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad, deactivate: false)) } func testLoadingAfterTimeBackground() { let lastLoad = Date(timeIntervalSinceNow: -ConfigManager.configBackgroundValidityInterval) - XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad)) + XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad, deactivate: false)) } func testDontLoadAfterTimeBackground() { let lastLoad = Date(timeIntervalSinceNow: -ConfigManager.configBackgroundValidityInterval + 1) - XCTAssertFalse(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad)) + XCTAssertFalse(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad, deactivate: false)) } func testLoadConfigAfterUrlChange() { - XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "newUrl", lastConfigUrl: "url", lastConfigLoad: .init())) + XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "newUrl", lastConfigUrl: "url", lastConfigLoad: .init(), deactivate: false)) } func testLoadConfigAfterUpdate() { - XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "newUrl", lastConfigUrl: nil, lastConfigLoad: .init())) + XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "newUrl", lastConfigUrl: nil, lastConfigLoad: .init(), deactivate: false)) + } + + func testAlwaysLoadConfigAfterDeactivate() { + let lastLoad = Date(timeIntervalSinceNow: -ConfigManager.configBackgroundValidityInterval + 1) + XCTAssert(ConfigManager.shouldLoadConfig(backgroundTask: true, url: "url", lastConfigUrl: "url", lastConfigLoad: lastLoad, deactivate: true)) } }