// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import UIKit import PromiseKit import SignalCoreKit import SignalUtilitiesKit import SessionUIKit import SessionUtilitiesKit 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.setTitleColor(theme.colors[.textPrimary], for: .normal) self?.unlockButton.setBackgroundImage( theme.colors[.textPrimary]?.withAlphaComponent(0.3).toImage(), for: .highlighted ) self?.unlockButton.layer.borderColor = theme.colors[.textPrimary]?.cgColor default: self?.unlockButton.setTitleColor(Theme.PrimaryColor.green.color, for: .normal) self?.unlockButton.setBackgroundImage( Theme.PrimaryColor.green.color.withAlphaComponent(0.3).toImage(), for: .highlighted ) self?.unlockButton.layer.borderColor = Theme.PrimaryColor.green.color.cgColor } } } 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() OWSAlerts.showAlert( // Title for alert indicating that screen lock could not be unlocked. title: "SCREEN_LOCK_UNLOCK_FAILED".localized(), message: message, buttonTitle: nil, buttonAction: { [weak self] action in // After the alert, update the UI self?.ensureUI() }, fromViewController: self ) } 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() } }