|  |  |  | // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import UIKit | 
					
						
							|  |  |  | import SignalCoreKit | 
					
						
							|  |  |  | import SignalUtilitiesKit | 
					
						
							|  |  |  | import SessionUIKit | 
					
						
							|  |  |  | import SessionUtilitiesKit | 
					
						
							|  |  |  | import SignalCoreKit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | final class SAEScreenLockViewController: ScreenLockViewController { | 
					
						
							|  |  |  |     private var hasShownAuthUIOnce: Bool = false | 
					
						
							|  |  |  |     private var isShowingAuthUI: Bool = false | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private weak var shareViewDelegate: ShareViewDelegate? | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - Initialization | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     init(shareViewDelegate: ShareViewDelegate) { | 
					
						
							|  |  |  |         super.init() | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.onUnlockPressed = { [weak self] in self?.unlockButtonWasTapped() } | 
					
						
							|  |  |  |         self.shareViewDelegate = shareViewDelegate | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     required init?(coder aDecoder: NSCoder) { | 
					
						
							|  |  |  |         fatalError("init(coder:) has not been implemented") | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     deinit { | 
					
						
							|  |  |  |         OWSLogger.verbose("Dealloc: \(type(of: self))") | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - UI | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var titleLabel: UILabel = { | 
					
						
							|  |  |  |         let titleLabel: UILabel = UILabel() | 
					
						
							|  |  |  |         titleLabel.font = UIFont.boldSystemFont(ofSize: Values.veryLargeFontSize) | 
					
						
							|  |  |  |         titleLabel.text = "vc_share_title".localized() | 
					
						
							|  |  |  |         titleLabel.themeTextColor = .textPrimary | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         return titleLabel | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var closeButton: UIBarButtonItem = { | 
					
						
							|  |  |  |         let closeButton: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "X"), style: .plain, target: self, action: #selector(dismissPressed)) | 
					
						
							|  |  |  |         closeButton.themeTintColor = .textPrimary | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         return closeButton | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - Lifecycle | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public override func loadView() { | 
					
						
							|  |  |  |         super.loadView() | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         UIView.appearance().themeTintColor = .textPrimary | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.view.themeBackgroundColor = .backgroundPrimary | 
					
						
							|  |  |  |         self.navigationItem.titleView = titleLabel | 
					
						
							|  |  |  |         self.navigationItem.leftBarButtonItem = closeButton | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         ThemeManager.onThemeChange(observer: self.unlockButton) { [weak self] theme, _ in | 
					
						
							|  |  |  |             switch theme.interfaceStyle { | 
					
						
							|  |  |  |                 case .light: | 
					
						
							|  |  |  |                     self?.unlockButton.setThemeTitleColorForced(.theme(theme, color: .textPrimary), for: .normal) | 
					
						
							|  |  |  |                     self?.unlockButton.setThemeBackgroundColorForced( | 
					
						
							|  |  |  |                         .theme(theme, color: .textPrimary), | 
					
						
							|  |  |  |                         for: .highlighted | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                     self?.unlockButton.themeBorderColorForced = .theme(theme, color: .textPrimary) | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     self?.unlockButton.setThemeTitleColorForced(.primary(.green), for: .normal) | 
					
						
							|  |  |  |                     self?.unlockButton.setThemeBackgroundColorForced( | 
					
						
							|  |  |  |                         .primary(.green, alpha: 0.3), | 
					
						
							|  |  |  |                         for: .highlighted | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                     self?.unlockButton.themeBorderColorForced = .primary(.green) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     override func viewWillAppear(_ animated: Bool) { | 
					
						
							|  |  |  |         super.viewWillAppear(animated) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.ensureUI() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     override func viewDidAppear(_ animated: Bool) { | 
					
						
							|  |  |  |         super.viewDidAppear(animated) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.ensureUI() | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Auto-show the auth UI f | 
					
						
							|  |  |  |         if !hasShownAuthUIOnce { | 
					
						
							|  |  |  |             hasShownAuthUIOnce = true | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             self.tryToPresentAuthUIToUnlockScreenLock() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - Functions | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private func tryToPresentAuthUIToUnlockScreenLock() { | 
					
						
							|  |  |  |         AssertIsOnMainThread() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // If we're already showing the auth UI; abort. | 
					
						
							|  |  |  |         if self.isShowingAuthUI { return } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         OWSLogger.info("try to unlock screen lock") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         isShowingAuthUI = true | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         ScreenLock.shared.tryToUnlockScreenLock( | 
					
						
							|  |  |  |             success: { [weak self] in | 
					
						
							|  |  |  |                 AssertIsOnMainThread() | 
					
						
							|  |  |  |                 OWSLogger.info("unlock screen lock succeeded.") | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 self?.isShowingAuthUI = false | 
					
						
							|  |  |  |                 self?.shareViewDelegate?.shareViewWasUnlocked() | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             failure: { [weak self] error in | 
					
						
							|  |  |  |                 AssertIsOnMainThread() | 
					
						
							|  |  |  |                 OWSLogger.info("unlock screen lock failed.") | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 self?.isShowingAuthUI = false | 
					
						
							|  |  |  |                 self?.ensureUI() | 
					
						
							|  |  |  |                 self?.showScreenLockFailureAlert(message: error.localizedDescription) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             unexpectedFailure: { [weak self] error in | 
					
						
							|  |  |  |                 AssertIsOnMainThread() | 
					
						
							|  |  |  |                 OWSLogger.info("unlock screen lock unexpectedly failed.") | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 self?.isShowingAuthUI = false | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Local Authentication isn't working properly. | 
					
						
							|  |  |  |                 // This isn't covered by the docs or the forums but in practice | 
					
						
							|  |  |  |                 // it appears to be effective to retry again after waiting a bit. | 
					
						
							|  |  |  |                 DispatchQueue.main.async { | 
					
						
							|  |  |  |                     self?.ensureUI() | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             cancel: { [weak self] in | 
					
						
							|  |  |  |                 AssertIsOnMainThread() | 
					
						
							|  |  |  |                 OWSLogger.info("unlock screen lock cancelled.") | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 self?.isShowingAuthUI = false | 
					
						
							|  |  |  |                 self?.ensureUI() | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.ensureUI() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private func ensureUI() { | 
					
						
							|  |  |  |         self.updateUI(state: .lock, animated: false) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private func showScreenLockFailureAlert(message: String) { | 
					
						
							|  |  |  |         AssertIsOnMainThread() | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         let modal: ConfirmationModal = ConfirmationModal( | 
					
						
							|  |  |  |             targetView: self.view, | 
					
						
							|  |  |  |             info: ConfirmationModal.Info( | 
					
						
							|  |  |  |                 title: "SCREEN_LOCK_UNLOCK_FAILED".localized(), | 
					
						
							|  |  |  |                 explanation: message, | 
					
						
							|  |  |  |                 cancelTitle: "BUTTON_OK".localized(), | 
					
						
							|  |  |  |                 cancelStyle: .alert_text, | 
					
						
							|  |  |  |                 afterClosed: { [weak self] in self?.ensureUI() } // After the alert, update the UI | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         self.present(modal, animated: true) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     func unlockButtonWasTapped() { | 
					
						
							|  |  |  |         AssertIsOnMainThread() | 
					
						
							|  |  |  |         OWSLogger.info("unlockButtonWasTapped") | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.tryToPresentAuthUIToUnlockScreenLock() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - Transitions | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     @objc private func dismissPressed() { | 
					
						
							|  |  |  |         OWSLogger.debug("unlock screen lock cancelled.") | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.cancelShareExperience() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private func cancelShareExperience() { | 
					
						
							|  |  |  |         self.shareViewDelegate?.shareViewWasCancelled() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |