diff options
Diffstat (limited to 'Sources/WireGuardApp/UI/iOS/ViewController/SettingsTableViewController.swift')
-rw-r--r-- | Sources/WireGuardApp/UI/iOS/ViewController/SettingsTableViewController.swift | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/Sources/WireGuardApp/UI/iOS/ViewController/SettingsTableViewController.swift b/Sources/WireGuardApp/UI/iOS/ViewController/SettingsTableViewController.swift new file mode 100644 index 0000000..483b779 --- /dev/null +++ b/Sources/WireGuardApp/UI/iOS/ViewController/SettingsTableViewController.swift @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2023 WireGuard LLC. All Rights Reserved. + +import UIKit +import os.log + +class SettingsTableViewController: UITableViewController { + + enum SettingsFields { + case iosAppVersion + case goBackendVersion + case exportZipArchive + case viewLog + + var localizedUIString: String { + switch self { + case .iosAppVersion: return tr("settingsVersionKeyWireGuardForIOS") + case .goBackendVersion: return tr("settingsVersionKeyWireGuardGoBackend") + case .exportZipArchive: return tr("settingsExportZipButtonTitle") + case .viewLog: return tr("settingsViewLogButtonTitle") + } + } + } + + let settingsFieldsBySection: [[SettingsFields]] = [ + [.iosAppVersion, .goBackendVersion], + [.exportZipArchive], + [.viewLog] + ] + + let tunnelsManager: TunnelsManager? + var wireguardCaptionedImage: (view: UIView, size: CGSize)? + + init(tunnelsManager: TunnelsManager?) { + self.tunnelsManager = tunnelsManager + super.init(style: .grouped) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + title = tr("settingsViewTitle") + navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped)) + + tableView.estimatedRowHeight = 44 + tableView.rowHeight = UITableView.automaticDimension + tableView.allowsSelection = false + + tableView.register(KeyValueCell.self) + tableView.register(ButtonCell.self) + + tableView.tableFooterView = UIImageView(image: UIImage(named: "wireguard.pdf")) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + guard let logo = tableView.tableFooterView else { return } + + let bottomPadding = max(tableView.layoutMargins.bottom, 10) + let fullHeight = max(tableView.contentSize.height, tableView.bounds.size.height - tableView.layoutMargins.top - bottomPadding) + + let imageAspectRatio = logo.intrinsicContentSize.width / logo.intrinsicContentSize.height + + var height = tableView.estimatedRowHeight * 1.5 + var width = height * imageAspectRatio + let maxWidth = view.bounds.size.width - max(tableView.layoutMargins.left + tableView.layoutMargins.right, 20) + if width > maxWidth { + width = maxWidth + height = width / imageAspectRatio + } + + let needsReload = height != logo.frame.height + + logo.frame = CGRect(x: (view.bounds.size.width - width) / 2, y: fullHeight - height, width: width, height: height) + + if needsReload { + tableView.tableFooterView = logo + } + } + + @objc func doneTapped() { + dismiss(animated: true, completion: nil) + } + + func exportConfigurationsAsZipFile(sourceView: UIView) { + PrivateDataConfirmation.confirmAccess(to: tr("iosExportPrivateData")) { [weak self] in + guard let self = self else { return } + guard let tunnelsManager = self.tunnelsManager else { return } + guard let destinationDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return } + + let destinationURL = destinationDir.appendingPathComponent("wireguard-export.zip") + _ = FileManager.deleteFile(at: destinationURL) + + let count = tunnelsManager.numberOfTunnels() + let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration } + ZipExporter.exportConfigFiles(tunnelConfigurations: tunnelConfigurations, to: destinationURL) { [weak self] error in + if let error = error { + ErrorPresenter.showErrorAlert(error: error, from: self) + return + } + + let fileExportVC = UIDocumentPickerViewController(url: destinationURL, in: .exportToService) + self?.present(fileExportVC, animated: true, completion: nil) + } + } + } + + func presentLogView() { + let logVC = LogViewController() + navigationController?.pushViewController(logVC, animated: true) + + } +} + +extension SettingsTableViewController { + override func numberOfSections(in tableView: UITableView) -> Int { + return settingsFieldsBySection.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return settingsFieldsBySection[section].count + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + switch section { + case 0: + return tr("settingsSectionTitleAbout") + case 1: + return tr("settingsSectionTitleExportConfigurations") + case 2: + return tr("settingsSectionTitleTunnelLog") + default: + return nil + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let field = settingsFieldsBySection[indexPath.section][indexPath.row] + if field == .iosAppVersion || field == .goBackendVersion { + let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath) + cell.copyableGesture = false + cell.key = field.localizedUIString + if field == .iosAppVersion { + var appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "Unknown version" + if let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String { + appVersion += " (\(appBuild))" + } + cell.value = appVersion + } else if field == .goBackendVersion { + cell.value = WIREGUARD_GO_VERSION + } + return cell + } else if field == .exportZipArchive { + let cell: ButtonCell = tableView.dequeueReusableCell(for: indexPath) + cell.buttonText = field.localizedUIString + cell.onTapped = { [weak self] in + self?.exportConfigurationsAsZipFile(sourceView: cell.button) + } + return cell + } else if field == .viewLog { + let cell: ButtonCell = tableView.dequeueReusableCell(for: indexPath) + cell.buttonText = field.localizedUIString + cell.onTapped = { [weak self] in + self?.presentLogView() + } + return cell + } + fatalError() + } +} |