summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2023-07-26 09:07:15 +0100
committerBug Magnet <marco.nikic@mullvad.net>2023-08-09 09:26:39 +0200
commita9e7d9240e19e651cde51e5ab2b28ffe334181b3 (patch)
treee6104df8e4c2a1bac519ba3006ba04d5c902ec54
parent5d00739e09600c4e525c3b5687ddb4f7ec3931fe (diff)
downloadmullvadvpn-a9e7d9240e19e651cde51e5ab2b28ffe334181b3.tar.xz
mullvadvpn-a9e7d9240e19e651cde51e5ab2b28ffe334181b3.zip
Add a UI for wireguard obfuscation preferences
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj1
-rw-r--r--ios/MullvadVPN/View controllers/Preferences/PreferencesCellFactory.swift47
-rw-r--r--ios/MullvadVPN/View controllers/Preferences/PreferencesDataSource.swift138
-rw-r--r--ios/MullvadVPN/View controllers/Preferences/PreferencesViewController.swift16
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SettingsHeaderView.swift3
5 files changed, 191 insertions, 14 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 464240306d..d71fb082c5 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -1618,6 +1618,7 @@
5867770F290975E8006F721F /* SettingsInteractorFactory.swift */,
58ACF64A26553C3F00ACE4B7 /* SettingsSwitchCell.swift */,
58CCA01122424D11004F3011 /* SettingsViewController.swift */,
+ 7A42DEC82A05164100B209BE /* SettingsInputCell.swift */,
);
path = Settings;
sourceTree = "<group>";
diff --git a/ios/MullvadVPN/View controllers/Preferences/PreferencesCellFactory.swift b/ios/MullvadVPN/View controllers/Preferences/PreferencesCellFactory.swift
index 7223808867..6ccefff8ac 100644
--- a/ios/MullvadVPN/View controllers/Preferences/PreferencesCellFactory.swift
+++ b/ios/MullvadVPN/View controllers/Preferences/PreferencesCellFactory.swift
@@ -245,6 +245,53 @@ final class PreferencesCellFactory: CellFactoryProtocol {
isEditing: isEditing,
preferredFont: .systemFont(ofSize: 14)
)
+ case .wireGuardObfuscationAutomatic:
+ guard let cell = cell as? SelectableSettingsCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "WIRE_GUARD_OBFUSCATION_AUTOMATIC_LABEL",
+ tableName: "Preferences",
+ value: "Automatic",
+ comment: ""
+ )
+ cell.accessibilityHint = nil
+ cell.applySubCellStyling()
+ case .wireGuardObfuscationOn:
+ guard let cell = cell as? SelectableSettingsCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "WIRE_GUARD_OBFUSCATION_ON_LABEL",
+ tableName: "Preferences",
+ value: "On (UDP-over-TCP)",
+ comment: ""
+ )
+ cell.accessibilityHint = nil
+ cell.applySubCellStyling()
+ case .wireGuardObfuscationOff:
+ guard let cell = cell as? SelectableSettingsCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "WIRE_GUARD_OBFUSCATION_OFF_LABEL",
+ tableName: "Preferences",
+ value: "Off",
+ comment: ""
+ )
+ cell.accessibilityHint = nil
+ cell.applySubCellStyling()
+
+ case let .wireGuardObfuscationPort(port):
+ guard let cell = cell as? SelectableSettingsCell else { return }
+
+ let portValue = port == 0 ? "Automatic" : "\(port)"
+
+ cell.titleLabel.text = NSLocalizedString(
+ "WIRE_GUARD_OBFUSCATION_PORT_LABEL",
+ tableName: "Preferences",
+ value: portValue,
+ comment: ""
+ )
+ cell.accessibilityHint = nil
+ cell.applySubCellStyling()
}
}
diff --git a/ios/MullvadVPN/View controllers/Preferences/PreferencesDataSource.swift b/ios/MullvadVPN/View controllers/Preferences/PreferencesDataSource.swift
index 8927d2107c..73f051597c 100644
--- a/ios/MullvadVPN/View controllers/Preferences/PreferencesDataSource.swift
+++ b/ios/MullvadVPN/View controllers/Preferences/PreferencesDataSource.swift
@@ -22,6 +22,8 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
case addDNSServer
case wireGuardPort
case wireGuardCustomPort
+ case wireGuardObfuscation
+ case wireGuardObfuscationPort
var reusableViewClass: AnyClass {
switch self {
@@ -39,6 +41,10 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
return SelectableSettingsCell.self
case .wireGuardCustomPort:
return SettingsInputCell.self
+ case .wireGuardObfuscation:
+ return SelectableSettingsCell.self
+ case .wireGuardObfuscationPort:
+ return SelectableSettingsCell.self
}
}
}
@@ -62,12 +68,16 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
case contentBlockers
case blockMalware
case wireGuardPorts
+ case wireGuardObfuscation
+ case wireGuardObfuscationPort
}
enum Section: String, Hashable, CaseIterable {
case contentBlockers
case customDNS
case wireGuardPorts
+ case wireGuardObfuscation
+ case wireGuardObfuscationPort
}
enum Item: Hashable {
@@ -82,6 +92,10 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
case addDNSServer
case dnsServer(_ uniqueID: UUID)
case dnsServerInfo
+ case wireGuardObfuscationAutomatic
+ case wireGuardObfuscationOn
+ case wireGuardObfuscationOff
+ case wireGuardObfuscationPort(_ port: UInt16)
static var contentBlockers: [Item] {
[.blockAdvertising, .blockTracking, .blockMalware, .blockAdultContent, .blockGambling]
@@ -94,6 +108,14 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
return [.wireGuardPort(nil)] + defaultPorts + [.wireGuardCustomPort]
}
+ static var wireGuardObfuscation: [Item] {
+ [.wireGuardObfuscationAutomatic, .wireGuardObfuscationOn, wireGuardObfuscationOff]
+ }
+
+ static var wireGuardObfuscationPort: [Item] {
+ [.wireGuardObfuscationPort(0), wireGuardObfuscationPort(80), wireGuardObfuscationPort(5001)]
+ }
+
var accessibilityIdentifier: String {
switch self {
case .blockAdvertising:
@@ -122,6 +144,17 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
return "dnsServer(\(uuid.uuidString))"
case .dnsServerInfo:
return "dnsServerInfo"
+ case .wireGuardObfuscationAutomatic:
+ return "Automatic"
+ case .wireGuardObfuscationOn:
+ return "On (UDP-over-TCP)"
+ case .wireGuardObfuscationOff:
+ return "Off"
+ case let .wireGuardObfuscationPort(port):
+ if port == 0 {
+ return "Automatic"
+ }
+ return "\(port)"
}
}
@@ -145,6 +178,10 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
return .wireGuardPort
case .wireGuardCustomPort:
return .wireGuardCustomPort
+ case .wireGuardObfuscationAutomatic, .wireGuardObfuscationOn, .wireGuardObfuscationOff:
+ return .wireGuardObfuscation
+ case .wireGuardObfuscationPort:
+ return .wireGuardObfuscationPort
default:
return .settingSwitch
}
@@ -331,7 +368,8 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
switch itemIdentifier(for: indexPath) {
- case .wireGuardPort, .wireGuardCustomPort:
+ case .wireGuardPort, .wireGuardCustomPort, .wireGuardObfuscationAutomatic, .wireGuardObfuscationOn,
+ .wireGuardObfuscationOff, .wireGuardObfuscationPort:
return true
default:
return false
@@ -341,6 +379,8 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = itemIdentifier(for: indexPath)
+ deselectAllRowsInSectionExceptRowAt(indexPath)
+
switch item {
case let .wireGuardPort(port):
viewModel.setWireGuardPort(port)
@@ -355,6 +395,15 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
case .wireGuardCustomPort:
getCustomPortCell()?.textField.becomeFirstResponder()
+ case .wireGuardObfuscationAutomatic:
+ print("UDP over TCP Set to automatic")
+ case .wireGuardObfuscationOn:
+ print("turning on UDP over TCP")
+ case .wireGuardObfuscationOff:
+ print("Turning off UDP over TCP")
+ case let .wireGuardObfuscationPort(port):
+ print("Setting port to \(port)")
+
default:
break
}
@@ -363,23 +412,27 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionIdentifier = snapshot().sectionIdentifiers[section]
+ guard let view = tableView
+ .dequeueReusableHeaderFooterView(
+ withIdentifier: HeaderFooterReuseIdentifiers.contentBlockerHeader
+ .rawValue
+ ) as? SettingsHeaderView else { return nil }
+
switch sectionIdentifier {
case .contentBlockers:
- guard let view = tableView
- .dequeueReusableHeaderFooterView(
- withIdentifier: HeaderFooterReuseIdentifiers.contentBlockerHeader.rawValue
- ) as? SettingsHeaderView else { return nil }
configureContentBlockersHeader(view)
return view
case .wireGuardPorts:
- guard let view = tableView
- .dequeueReusableHeaderFooterView(
- withIdentifier: HeaderFooterReuseIdentifiers.contentBlockerHeader.rawValue
- ) as? SettingsHeaderView else { return nil }
configureWireguardPortsHeader(view)
return view
+ case .wireGuardObfuscation:
+ configureObfuscationHeader(view)
+ return view
+ case .wireGuardObfuscationPort:
+ configureObfuscationPortHeader(view)
+ return view
default:
return nil
}
@@ -389,14 +442,10 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
let sectionIdentifier = snapshot().sectionIdentifiers[section]
switch sectionIdentifier {
- case .contentBlockers:
- return nil
-
case .wireGuardPorts:
return tableView.dequeueReusableHeaderFooterView(
withIdentifier: HeaderFooterReuseIdentifiers.spacer.rawValue
)
-
default:
return nil
}
@@ -418,7 +467,7 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
let sectionIdentifier = snapshot().sectionIdentifiers[section]
switch sectionIdentifier {
- case .wireGuardPorts:
+ case .wireGuardObfuscationPort:
return UIMetrics.sectionSpacing
default:
@@ -492,6 +541,15 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
}
}
+ private func deselectAllRowsInSectionExceptRowAt(_ indexPath: IndexPath) {
+ guard let indexPaths = tableView?.indexPathsForSelectedRows else { return }
+ let rowsToDeselect = indexPaths.filter { $0.section == indexPath.section && $0.row != indexPath.row }
+
+ rowsToDeselect.forEach {
+ tableView?.deselectRow(at: $0, animated: false)
+ }
+ }
+
private func updateSnapshot(animated: Bool = false, completion: (() -> Void)? = nil) {
var newSnapshot = NSDiffableDataSourceSnapshot<Section, Item>()
let oldSnapshot = snapshot()
@@ -783,6 +841,58 @@ final class PreferencesDataSource: UITableViewDiffableDataSource<
}
}
+ private func configureObfuscationHeader(_ header: SettingsHeaderView) {
+ header.titleLabel.text = NSLocalizedString(
+ "OBFUSCATION_HEADER_LABEL",
+ tableName: "Preferences",
+ value: "WireGuard Obfuscation",
+ comment: ""
+ )
+
+ header.didCollapseHandler = { [weak self] header in
+ guard let self else { return }
+
+ var snapshot = snapshot()
+ if header.isExpanded {
+ snapshot.deleteItems(Item.wireGuardObfuscation)
+ } else {
+ snapshot.appendItems(Item.wireGuardObfuscation, toSection: .wireGuardObfuscation)
+ }
+ header.isExpanded.toggle()
+ applySnapshot(snapshot, animated: true)
+ }
+
+ header.infoButtonHandler = { [weak self] in
+ self.map { $0.delegate?.preferencesDataSource($0, showInfo: .wireGuardObfuscation) }
+ }
+ }
+
+ private func configureObfuscationPortHeader(_ header: SettingsHeaderView) {
+ header.titleLabel.text = NSLocalizedString(
+ "OBFUSCATION_PORT_HEADER_LABEL",
+ tableName: "Preferences",
+ value: "UDP-over-TCP Port",
+ comment: ""
+ )
+
+ header.didCollapseHandler = { [weak self] header in
+ guard let self else { return }
+
+ var snapshot = snapshot()
+ if header.isExpanded {
+ snapshot.deleteItems(Item.wireGuardObfuscationPort)
+ } else {
+ snapshot.appendItems(Item.wireGuardObfuscationPort, toSection: .wireGuardObfuscationPort)
+ }
+ header.isExpanded.toggle()
+ applySnapshot(snapshot, animated: true)
+ }
+
+ header.infoButtonHandler = { [weak self] in
+ self.map { $0.delegate?.preferencesDataSource($0, showInfo: .wireGuardObfuscationPort) }
+ }
+ }
+
private func selectRow(at indexPath: IndexPath?, animated: Bool = false) {
tableView?.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
diff --git a/ios/MullvadVPN/View controllers/Preferences/PreferencesViewController.swift b/ios/MullvadVPN/View controllers/Preferences/PreferencesViewController.swift
index c6c3bfc218..4410c47d1d 100644
--- a/ios/MullvadVPN/View controllers/Preferences/PreferencesViewController.swift
+++ b/ios/MullvadVPN/View controllers/Preferences/PreferencesViewController.swift
@@ -35,6 +35,7 @@ class PreferencesViewController: UITableViewController, PreferencesDataSourceDel
tableView.estimatedRowHeight = 60
tableView.estimatedSectionHeaderHeight = tableView.estimatedRowHeight
tableView.allowsSelectionDuringEditing = true
+ tableView.allowsMultipleSelection = true
dataSource = PreferencesDataSource(tableView: tableView)
dataSource?.delegate = self
@@ -161,6 +162,21 @@ class PreferencesViewController: UITableViewController, PreferencesDataSourceDel
portsString
)
+ case .wireGuardObfuscation:
+ message = NSLocalizedString(
+ "PREFERENCES_WIRE_GUARD_OBFUSCATION_GENERAL",
+ tableName: "WireGuardObfuscation",
+ value: "Obfuscation hides the WireGuard traffic inside another protocol. It can be used to help circumvent censorship and other types of filtering, where a plain WireGuard connect would be blocked.",
+ comment: ""
+ )
+
+ case .wireGuardObfuscationPort:
+ message = NSLocalizedString(
+ "PREFERENCES_WIRE_GUARD_OBFUSCATION_PORT_GENERAL",
+ tableName: "WireGuardObfuscation",
+ value: "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server.",
+ comment: ""
+ )
default:
assertionFailure("No matching InfoButtonItem")
}
diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsHeaderView.swift b/ios/MullvadVPN/View controllers/Settings/SettingsHeaderView.swift
index 51bb8e2742..4a536528a5 100644
--- a/ios/MullvadVPN/View controllers/Settings/SettingsHeaderView.swift
+++ b/ios/MullvadVPN/View controllers/Settings/SettingsHeaderView.swift
@@ -119,6 +119,9 @@ class SettingsHeaderView: UITableViewHeaderFooterView {
}
private func updateAccessibilityCustomActions() {
+ #warning(
+ "SettingsHeaderView is reused for more than content blockers now, this code doesn't seem correct anymore."
+ )
let actionName = isExpanded
? NSLocalizedString(
"CONTENT_BLOCKERS_COLLAPSE_ACCESSIBILITY_ACTION",