// 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() } }