on-demand: iOS: Tunnel detail: Show SSID info

Signed-off-by: Roopesh Chander <roop@roopc.net>
This commit is contained in:
Roopesh Chander 2019-03-05 19:25:17 +05:30 committed by Jason A. Donenfeld
parent bdeb89a9e5
commit 01604dd8d1
4 changed files with 80 additions and 17 deletions

View File

@ -84,9 +84,7 @@
"tunnelOnDemandCellular" = "Cellular"; "tunnelOnDemandCellular" = "Cellular";
"tunnelOnDemandEthernet" = "Ethernet"; "tunnelOnDemandEthernet" = "Ethernet";
"tunnelOnDemandWiFi" = "Wi-Fi"; "tunnelOnDemandWiFi" = "Wi-Fi";
"tunnelOnDemandSSIDDescription" = "Selected SSIDs"; "tunnelOnDemandSSIDsKey" = "SSIDs";
"tunnelOnDemandSSIDescriptionMac" = "SSIDs";
"tunnelOnDemandSSIDEdit" = "Select SSIDs";
"tunnelOnDemandAnySSID" = "Any SSID"; "tunnelOnDemandAnySSID" = "Any SSID";
"tunnelOnDemandOnlySelectedSSIDs" = "Only selected SSIDs"; "tunnelOnDemandOnlySelectedSSIDs" = "Only selected SSIDs";
@ -98,7 +96,7 @@
"tunnelOnDemandAddMessageAddNewSSID" = "Add manually"; "tunnelOnDemandAddMessageAddNewSSID" = "Add manually";
"tunnelOnDemandAddMessageAddConnectedSSID (%@)" = "Connected: %@"; "tunnelOnDemandAddMessageAddConnectedSSID (%@)" = "Connected: %@";
"tunnelOnDemandKey" = "Activate on demand"; "tunnelOnDemandKey" = "On demand";
"tunnelOnDemandOptionOff" = "Off"; "tunnelOnDemandOptionOff" = "Off";
"tunnelOnDemandOptionWiFiOnly" = "Wi-Fi only"; "tunnelOnDemandOptionWiFiOnly" = "Wi-Fi only";
"tunnelOnDemandOptionWiFiOrCellular" = "Wi-Fi or cellular"; "tunnelOnDemandOptionWiFiOrCellular" = "Wi-Fi or cellular";

View File

@ -5,13 +5,15 @@ import Foundation
class ActivateOnDemandViewModel { class ActivateOnDemandViewModel {
enum OnDemandField { enum OnDemandField {
case onDemand
case nonWiFiInterface case nonWiFiInterface
case wiFiInterface case wiFiInterface
case ssidDescription case ssid
case ssidEdit
var localizedUIString: String { var localizedUIString: String {
switch self { switch self {
case .onDemand:
return tr("tunnelOnDemandKey")
case .nonWiFiInterface: case .nonWiFiInterface:
#if os(iOS) #if os(iOS)
return tr("tunnelOnDemandCellular") return tr("tunnelOnDemandCellular")
@ -21,8 +23,7 @@ class ActivateOnDemandViewModel {
#error("Unimplemented") #error("Unimplemented")
#endif #endif
case .wiFiInterface: return tr("tunnelOnDemandWiFi") case .wiFiInterface: return tr("tunnelOnDemandWiFi")
case .ssidDescription: return tr("tunnelOnDemandSSIDDescription") case .ssid: return tr("tunnelOnDemandSSIDsKey")
case .ssidEdit: return tr("tunnelOnDemandSSIDEdit")
} }
} }
} }
@ -48,7 +49,15 @@ class ActivateOnDemandViewModel {
} }
extension ActivateOnDemandViewModel { extension ActivateOnDemandViewModel {
convenience init(from option: ActivateOnDemandOption) { convenience init(setting: ActivateOnDemandSetting) {
if setting.isActivateOnDemandEnabled {
self.init(option: setting.activateOnDemandOption)
} else {
self.init(option: .none)
}
}
convenience init(option: ActivateOnDemandOption) {
self.init() self.init()
switch option { switch option {
case .none: case .none:
@ -103,6 +112,33 @@ extension ActivateOnDemandViewModel {
} }
} }
extension ActivateOnDemandViewModel {
var localizedInterfaceDescription: String {
switch (isWiFiInterfaceEnabled, isNonWiFiInterfaceEnabled) {
case (false, false):
return tr("tunnelOnDemandOptionOff")
case (true, false):
return tr("tunnelOnDemandOptionWiFiOnly")
case (false, true):
#if os(iOS)
return tr("tunnelOnDemandOptionCellularOnly")
#elseif os(macOS)
return tr("tunnelOnDemandOptionEthernetOnly")
#else
#error("Unimplemented")
#endif
case (true, true):
#if os(iOS)
return tr("tunnelOnDemandOptionWiFiOrCellular")
#elseif os(macOS)
return tr("tunnelOnDemandOptionWiFiOrEthernet")
#else
#error("Unimplemented")
#endif
}
}
}
private extension ActivateOnDemandViewModel { private extension ActivateOnDemandViewModel {
func ssidViewModel(from ssidOption: ActivateOnDemandSSIDOption) -> (OnDemandSSIDOption, [String]) { func ssidViewModel(from ssidOption: ActivateOnDemandSSIDOption) -> (OnDemandSSIDOption, [String]) {
switch ssidOption { switch ssidOption {

View File

@ -24,21 +24,28 @@ class TunnelDetailTableViewController: UITableViewController {
.rxBytes, .txBytes, .lastHandshakeTime .rxBytes, .txBytes, .lastHandshakeTime
] ]
static let onDemandFields: [ActivateOnDemandViewModel.OnDemandField] = [
.onDemand, .ssid
]
let tunnelsManager: TunnelsManager let tunnelsManager: TunnelsManager
let tunnel: TunnelContainer let tunnel: TunnelContainer
var tunnelViewModel: TunnelViewModel var tunnelViewModel: TunnelViewModel
var onDemandViewModel: ActivateOnDemandViewModel
private var sections = [Section]() private var sections = [Section]()
private var interfaceFieldIsVisible = [Bool]() private var interfaceFieldIsVisible = [Bool]()
private var peerFieldIsVisible = [[Bool]]() private var peerFieldIsVisible = [[Bool]]()
private var statusObservationToken: AnyObject? private var statusObservationToken: AnyObject?
private var onDemandObservationToken: AnyObject?
private var reloadRuntimeConfigurationTimer: Timer? private var reloadRuntimeConfigurationTimer: Timer?
init(tunnelsManager: TunnelsManager, tunnel: TunnelContainer) { init(tunnelsManager: TunnelsManager, tunnel: TunnelContainer) {
self.tunnelsManager = tunnelsManager self.tunnelsManager = tunnelsManager
self.tunnel = tunnel self.tunnel = tunnel
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration) tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration)
onDemandViewModel = ActivateOnDemandViewModel(setting: tunnel.activateOnDemandSetting)
super.init(style: .grouped) super.init(style: .grouped)
loadSections() loadSections()
loadVisibleFields() loadVisibleFields()
@ -51,6 +58,11 @@ class TunnelDetailTableViewController: UITableViewController {
self.stopUpdatingRuntimeConfiguration() self.stopUpdatingRuntimeConfiguration()
} }
} }
onDemandObservationToken = tunnel.observe(\.isActivateOnDemandEnabled) { [weak self] tunnel, _ in
// Handle On-Demand getting turned on/off outside of the app
self?.onDemandViewModel = ActivateOnDemandViewModel(setting: tunnel.activateOnDemandSetting)
self?.updateActivateOnDemandFields()
}
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
@ -242,11 +254,27 @@ class TunnelDetailTableViewController: UITableViewController {
self.applyTunnelConfiguration(tunnelConfiguration: tunnelConfiguration) self.applyTunnelConfiguration(tunnelConfiguration: tunnelConfiguration)
} }
} }
private func updateActivateOnDemandFields() {
guard let onDemandSection = sections.firstIndex(where: { if case .onDemand = $0 { return true } else { return false } }) else { return }
let numberOfTableViewOnDemandRows = tableView.numberOfRows(inSection: onDemandSection)
let ssidRowIndexPath = IndexPath(row: 1, section: onDemandSection)
switch (numberOfTableViewOnDemandRows, onDemandViewModel.isWiFiInterfaceEnabled) {
case (1, true):
tableView.insertRows(at: [ssidRowIndexPath], with: .automatic)
case (2, false):
tableView.deleteRows(at: [ssidRowIndexPath], with: .automatic)
default:
break
}
tableView.reloadSections(IndexSet(integer: onDemandSection), with: .automatic)
}
} }
extension TunnelDetailTableViewController: TunnelEditTableViewControllerDelegate { extension TunnelDetailTableViewController: TunnelEditTableViewControllerDelegate {
func tunnelSaved(tunnel: TunnelContainer) { func tunnelSaved(tunnel: TunnelContainer) {
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration) tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration)
onDemandViewModel = ActivateOnDemandViewModel(setting: tunnel.activateOnDemandSetting)
loadSections() loadSections()
loadVisibleFields() loadVisibleFields()
title = tunnel.name title = tunnel.name
@ -272,7 +300,7 @@ extension TunnelDetailTableViewController {
case .peer(let peerIndex, _): case .peer(let peerIndex, _):
return peerFieldIsVisible[peerIndex].filter { $0 }.count return peerFieldIsVisible[peerIndex].filter { $0 }.count
case .onDemand: case .onDemand:
return 1 return onDemandViewModel.isWiFiInterfaceEnabled ? 2 : 1
case .delete: case .delete:
return 1 return 1
} }
@ -380,10 +408,12 @@ extension TunnelDetailTableViewController {
private func onDemandCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell { private func onDemandCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath) let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
cell.key = tr("tunnelOnDemandKey") let field = TunnelDetailTableViewController.onDemandFields[indexPath.row]
cell.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting) cell.key = field.localizedUIString
cell.observationToken = tunnel.observe(\.isActivateOnDemandEnabled) { [weak cell] tunnel, _ in if field == .onDemand {
cell?.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting) cell.value = onDemandViewModel.localizedInterfaceDescription
} else if field == .ssid {
cell.value = onDemandViewModel.ssidOption.localizedUIString
} }
return cell return cell
} }

View File

@ -46,7 +46,7 @@ class TunnelEditTableViewController: UITableViewController {
let onDemandFields: [ActivateOnDemandViewModel.OnDemandField] = [ let onDemandFields: [ActivateOnDemandViewModel.OnDemandField] = [
.nonWiFiInterface, .nonWiFiInterface,
.wiFiInterface, .wiFiInterface,
.ssidEdit .ssid
] ]
let tunnelsManager: TunnelsManager let tunnelsManager: TunnelsManager
@ -60,8 +60,7 @@ class TunnelEditTableViewController: UITableViewController {
self.tunnelsManager = tunnelsManager self.tunnelsManager = tunnelsManager
self.tunnel = tunnel self.tunnel = tunnel
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration) tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration)
let onDemandOption = tunnel.activateOnDemandSetting.isActivateOnDemandEnabled ? tunnel.activateOnDemandSetting.activateOnDemandOption : .none onDemandViewModel = ActivateOnDemandViewModel(setting: tunnel.activateOnDemandSetting)
onDemandViewModel = ActivateOnDemandViewModel(from: onDemandOption)
super.init(style: .grouped) super.init(style: .grouped)
loadSections() loadSections()
} }