Merge pull request #51 from passepartoutvpn/track-exchanged-bytes-regularly
Track exchanged bytes regularly
This commit is contained in:
commit
b1ec14d2c3
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Siri Shortcuts in-app manager. [#46](https://github.com/passepartoutvpn/passepartout-ios/pull/46)
|
- Siri Shortcuts in-app manager. [#46](https://github.com/passepartoutvpn/passepartout-ios/pull/46)
|
||||||
|
- Background data count updates in diagnostics. [#51](https://github.com/passepartoutvpn/passepartout-ios/pull/51)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
//
|
||||||
|
// UtilsTests.swift
|
||||||
|
// Passepartout-CoreTests
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 3/30/19.
|
||||||
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import Passepartout_Core
|
||||||
|
|
||||||
|
class UtilsTests: XCTestCase {
|
||||||
|
override func setUp() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() {
|
||||||
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDataUnitDescription() {
|
||||||
|
XCTAssertEqual(0.dataUnitDescription, "0B")
|
||||||
|
XCTAssertEqual(1.dataUnitDescription, "1B")
|
||||||
|
XCTAssertEqual(1024.dataUnitDescription, "1kB")
|
||||||
|
XCTAssertEqual(1025.dataUnitDescription, "1kB")
|
||||||
|
XCTAssertEqual(548575.dataUnitDescription, "0.52MB")
|
||||||
|
XCTAssertEqual(1048575.dataUnitDescription, "1.00MB")
|
||||||
|
XCTAssertEqual(1048576.dataUnitDescription, "1.00MB")
|
||||||
|
XCTAssertEqual(1048577.dataUnitDescription, "1.00MB")
|
||||||
|
XCTAssertEqual(600000000.dataUnitDescription, "0.56GB")
|
||||||
|
XCTAssertEqual(1073741823.dataUnitDescription, "1.00GB")
|
||||||
|
XCTAssertEqual(1073741824.dataUnitDescription, "1.00GB")
|
||||||
|
XCTAssertEqual(1073741825.dataUnitDescription, "1.00GB")
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ class PacketTunnelProvider: TunnelKitProvider {
|
||||||
appVersion = "\(GroupConstants.App.name) \(GroupConstants.App.versionString)"
|
appVersion = "\(GroupConstants.App.name) \(GroupConstants.App.versionString)"
|
||||||
dnsTimeout = GroupConstants.VPN.dnsTimeout
|
dnsTimeout = GroupConstants.VPN.dnsTimeout
|
||||||
logSeparator = GroupConstants.VPN.sessionMarker
|
logSeparator = GroupConstants.VPN.sessionMarker
|
||||||
|
dataCountInterval = GroupConstants.VPN.dataCountInterval
|
||||||
super.startTunnel(options: options, completionHandler: completionHandler)
|
super.startTunnel(options: options, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
private var shouldDeleteLogOnDisconnection = false
|
private var shouldDeleteLogOnDisconnection = false
|
||||||
|
|
||||||
|
private var currentDataCount: (Int, Int)?
|
||||||
|
|
||||||
// MARK: Table
|
// MARK: Table
|
||||||
|
|
||||||
var model: TableModel<SectionType, RowType> = TableModel()
|
var model: TableModel<SectionType, RowType> = TableModel()
|
||||||
|
@ -103,6 +105,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
nc.addObserver(self, selector: #selector(vpnDidUpdate), name: .VPNDidChangeStatus, object: nil)
|
nc.addObserver(self, selector: #selector(vpnDidUpdate), name: .VPNDidChangeStatus, object: nil)
|
||||||
nc.addObserver(self, selector: #selector(vpnDidUpdate), name: .VPNDidReinstall, object: nil)
|
nc.addObserver(self, selector: #selector(vpnDidUpdate), name: .VPNDidReinstall, object: nil)
|
||||||
nc.addObserver(self, selector: #selector(intentDidUpdateService), name: .IntentDidUpdateService, object: nil)
|
nc.addObserver(self, selector: #selector(intentDidUpdateService), name: .IntentDidUpdateService, object: nil)
|
||||||
|
nc.addObserver(self, selector: #selector(serviceDidUpdateDataCount(_:)), name: .ConnectionServiceDidUpdateDataCount, object: nil)
|
||||||
|
|
||||||
// run this no matter what
|
// run this no matter what
|
||||||
// XXX: convenient here vs AppDelegate for updating table
|
// XXX: convenient here vs AppDelegate for updating table
|
||||||
|
@ -373,32 +376,32 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func displayDataCount() {
|
// private func displayDataCount() {
|
||||||
guard vpn.isEnabled else {
|
// guard vpn.isEnabled else {
|
||||||
let alert = Macros.alert(
|
// let alert = Macros.alert(
|
||||||
L10n.Service.Cells.DataCount.caption,
|
// L10n.Service.Cells.DataCount.caption,
|
||||||
L10n.Service.Alerts.DataCount.Messages.notAvailable
|
// L10n.Service.Alerts.DataCount.Messages.notAvailable
|
||||||
)
|
// )
|
||||||
alert.addCancelAction(L10n.Global.ok)
|
// alert.addCancelAction(L10n.Global.ok)
|
||||||
present(alert, animated: true, completion: nil)
|
// present(alert, animated: true, completion: nil)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
vpn.requestBytesCount {
|
// vpn.requestBytesCount {
|
||||||
let message: String
|
// let message: String
|
||||||
if let count = $0 {
|
// if let count = $0 {
|
||||||
message = L10n.Service.Alerts.DataCount.Messages.current(Int(count.0), Int(count.1))
|
// message = L10n.Service.Alerts.DataCount.Messages.current(Int(count.0), Int(count.1))
|
||||||
} else {
|
// } else {
|
||||||
message = L10n.Service.Alerts.DataCount.Messages.notAvailable
|
// message = L10n.Service.Alerts.DataCount.Messages.notAvailable
|
||||||
}
|
// }
|
||||||
let alert = Macros.alert(
|
// let alert = Macros.alert(
|
||||||
L10n.Service.Cells.DataCount.caption,
|
// L10n.Service.Cells.DataCount.caption,
|
||||||
message
|
// message
|
||||||
)
|
// )
|
||||||
alert.addCancelAction(L10n.Global.ok)
|
// alert.addCancelAction(L10n.Global.ok)
|
||||||
self.present(alert, animated: true, completion: nil)
|
// self.present(alert, animated: true, completion: nil)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private func togglePrivateDataMasking(cell: ToggleTableViewCell) {
|
private func togglePrivateDataMasking(cell: ToggleTableViewCell) {
|
||||||
let handler = {
|
let handler = {
|
||||||
|
@ -468,6 +471,13 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
reloadModel()
|
reloadModel()
|
||||||
updateViewsIfNeeded()
|
updateViewsIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func serviceDidUpdateDataCount(_ notification: Notification) {
|
||||||
|
guard let dataCount = notification.userInfo?[ConnectionService.NotificationKeys.dataCount] as? (Int, Int) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
refreshDataCount(dataCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
@ -551,6 +561,10 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
return model.indexPath(row: .connectionStatus, section: .vpn)
|
return model.indexPath(row: .connectionStatus, section: .vpn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var dataCountIndexPath: IndexPath? {
|
||||||
|
return model.indexPath(row: .dataCount, section: .diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
private var endpointIndexPath: IndexPath {
|
private var endpointIndexPath: IndexPath {
|
||||||
guard let ip = model.indexPath(row: .endpoint, section: .configuration) else {
|
guard let ip = model.indexPath(row: .endpoint, section: .configuration) else {
|
||||||
fatalError("Could not locate endpointIndexPath")
|
fatalError("Could not locate endpointIndexPath")
|
||||||
|
@ -743,6 +757,13 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
case .dataCount:
|
case .dataCount:
|
||||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
||||||
cell.leftText = L10n.Service.Cells.DataCount.caption
|
cell.leftText = L10n.Service.Cells.DataCount.caption
|
||||||
|
if let count = currentDataCount, vpn.status == .connected {
|
||||||
|
cell.rightText = L10n.Service.Cells.DataCount.value(count.0.dataUnitDescription, count.1.dataUnitDescription)
|
||||||
|
} else {
|
||||||
|
cell.rightText = nil
|
||||||
|
}
|
||||||
|
cell.accessoryType = .none
|
||||||
|
cell.isTappable = false
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
case .debugLog:
|
case .debugLog:
|
||||||
|
@ -859,8 +880,8 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
case .testConnectivity:
|
case .testConnectivity:
|
||||||
testInternetConnectivity()
|
testInternetConnectivity()
|
||||||
|
|
||||||
case .dataCount:
|
// case .dataCount:
|
||||||
displayDataCount()
|
// displayDataCount()
|
||||||
|
|
||||||
case .debugLog:
|
case .debugLog:
|
||||||
perform(segue: StoryboardSegue.Main.debugLogSegueIdentifier, sender: cell)
|
perform(segue: StoryboardSegue.Main.debugLogSegueIdentifier, sender: cell)
|
||||||
|
@ -1018,10 +1039,24 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
guard service.isActiveProfile(profile) else {
|
guard service.isActiveProfile(profile) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var ips: [IndexPath] = []
|
||||||
guard let statusIndexPath = statusIndexPath else {
|
guard let statusIndexPath = statusIndexPath else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tableView.reloadRows(at: [statusIndexPath], with: .none)
|
ips.append(statusIndexPath)
|
||||||
|
if let dataCountIndexPath = dataCountIndexPath {
|
||||||
|
currentDataCount = service.vpnDataCount
|
||||||
|
ips.append(dataCountIndexPath)
|
||||||
|
}
|
||||||
|
tableView.reloadRows(at: ips, with: .none)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func refreshDataCount(_ dataCount: (Int, Int)?) {
|
||||||
|
currentDataCount = dataCount
|
||||||
|
guard let dataCountIndexPath = dataCountIndexPath else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tableView.reloadRows(at: [dataCountIndexPath], with: .none)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadSelectedRow(andRowAt indexPath: IndexPath? = nil) {
|
func reloadSelectedRow(andRowAt indexPath: IndexPath? = nil) {
|
||||||
|
|
|
@ -93,7 +93,7 @@ class TableModel<S: Hashable, R: Equatable> {
|
||||||
|
|
||||||
func indexPath(row rowObject: R, section sectionObject: S) -> IndexPath? {
|
func indexPath(row rowObject: R, section sectionObject: S) -> IndexPath? {
|
||||||
guard let sectionIndex = sections.index(of: sectionObject) else {
|
guard let sectionIndex = sections.index(of: sectionObject) else {
|
||||||
fatalError("Missing section: \(sectionObject)")
|
return nil
|
||||||
}
|
}
|
||||||
guard let row = rowsBySection[sectionObject]?.index(of: rowObject) else {
|
guard let row = rowsBySection[sectionObject]?.index(of: rowObject) else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -82,6 +82,8 @@
|
||||||
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */; };
|
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */; };
|
||||||
0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; };
|
0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; };
|
||||||
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */; };
|
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */; };
|
||||||
|
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB109224FECEA00E9E551 /* DataUnit.swift */; };
|
||||||
|
0ECEB10C224FEF9B00E9E551 /* UtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB10B224FEF9B00E9E551 /* UtilsTests.swift */; };
|
||||||
0ECEE44E20E1122200A6BB43 /* TableModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44D20E1122200A6BB43 /* TableModel.swift */; };
|
0ECEE44E20E1122200A6BB43 /* TableModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44D20E1122200A6BB43 /* TableModel.swift */; };
|
||||||
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */; };
|
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */; };
|
||||||
0ED31C2920CF2A340027975F /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2820CF2A340027975F /* AccountViewController.swift */; };
|
0ED31C2920CF2A340027975F /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2820CF2A340027975F /* AccountViewController.swift */; };
|
||||||
|
@ -226,6 +228,8 @@
|
||||||
0ECEB106224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Organizer.storyboard; sourceTree = "<group>"; };
|
0ECEB106224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Organizer.storyboard; sourceTree = "<group>"; };
|
||||||
0ECEB107224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Shortcuts.storyboard; sourceTree = "<group>"; };
|
0ECEB107224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Shortcuts.storyboard; sourceTree = "<group>"; };
|
||||||
0ECEB108224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
0ECEB108224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
0ECEB109224FECEA00E9E551 /* DataUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataUnit.swift; sourceTree = "<group>"; };
|
||||||
|
0ECEB10B224FEF9B00E9E551 /* UtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UtilsTests.swift; sourceTree = "<group>"; };
|
||||||
0ECEE44D20E1122200A6BB43 /* TableModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableModel.swift; sourceTree = "<group>"; };
|
0ECEE44D20E1122200A6BB43 /* TableModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableModel.swift; sourceTree = "<group>"; };
|
||||||
0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Cells.swift"; sourceTree = "<group>"; };
|
0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Cells.swift"; sourceTree = "<group>"; };
|
||||||
0ED31C0F20CF09A30027975F /* Pool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pool.swift; sourceTree = "<group>"; };
|
0ED31C0F20CF09A30027975F /* Pool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pool.swift; sourceTree = "<group>"; };
|
||||||
|
@ -338,6 +342,7 @@
|
||||||
0EBBE8F121822B4D00106008 /* ConnectionService.json */,
|
0EBBE8F121822B4D00106008 /* ConnectionService.json */,
|
||||||
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */,
|
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */,
|
||||||
0ED31C2620CF257C0027975F /* InfrastructureTests.swift */,
|
0ED31C2620CF257C0027975F /* InfrastructureTests.swift */,
|
||||||
|
0ECEB10B224FEF9B00E9E551 /* UtilsTests.swift */,
|
||||||
0E3152AC223F9EF500F61841 /* Info.plist */,
|
0E3152AC223F9EF500F61841 /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = "Passepartout-CoreTests";
|
path = "Passepartout-CoreTests";
|
||||||
|
@ -456,6 +461,7 @@
|
||||||
0E2D11B9217DBEDE0096822C /* ConnectionService+Configurations.swift */,
|
0E2D11B9217DBEDE0096822C /* ConnectionService+Configurations.swift */,
|
||||||
0EBBE8F42182361700106008 /* ConnectionService+Migration.swift */,
|
0EBBE8F42182361700106008 /* ConnectionService+Migration.swift */,
|
||||||
0EDE8DE620C93945004C739C /* Credentials.swift */,
|
0EDE8DE620C93945004C739C /* Credentials.swift */,
|
||||||
|
0ECEB109224FECEA00E9E551 /* DataUnit.swift */,
|
||||||
0EC7F20420E24308004EA58E /* DebugLog.swift */,
|
0EC7F20420E24308004EA58E /* DebugLog.swift */,
|
||||||
0ED38AE621404F100004D387 /* EndpointDataSource.swift */,
|
0ED38AE621404F100004D387 /* EndpointDataSource.swift */,
|
||||||
0E89DFC4213DF7AE00741BA1 /* Preferences.swift */,
|
0E89DFC4213DF7AE00741BA1 /* Preferences.swift */,
|
||||||
|
@ -942,6 +948,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */,
|
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */,
|
||||||
|
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */,
|
||||||
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */,
|
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */,
|
||||||
0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */,
|
0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */,
|
||||||
0E3152C4223FA04800F61841 /* VPN.swift in Sources */,
|
0E3152C4223FA04800F61841 /* VPN.swift in Sources */,
|
||||||
|
@ -986,6 +993,7 @@
|
||||||
files = (
|
files = (
|
||||||
0E3152BA223F9F3D00F61841 /* InfrastructureTests.swift in Sources */,
|
0E3152BA223F9F3D00F61841 /* InfrastructureTests.swift in Sources */,
|
||||||
0E3152B9223F9F3B00F61841 /* ConnectionServiceTests.swift in Sources */,
|
0E3152B9223F9F3B00F61841 /* ConnectionServiceTests.swift in Sources */,
|
||||||
|
0ECEB10C224FEF9B00E9E551 /* UtilsTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -107,7 +107,8 @@
|
||||||
"service.cells.trusted_add_wifi.caption" = "Add current Wi-Fi";
|
"service.cells.trusted_add_wifi.caption" = "Add current Wi-Fi";
|
||||||
"service.cells.trusted_policy.caption" = "Trust disables VPN";
|
"service.cells.trusted_policy.caption" = "Trust disables VPN";
|
||||||
"service.cells.test_connectivity.caption" = "Test connectivity";
|
"service.cells.test_connectivity.caption" = "Test connectivity";
|
||||||
"service.cells.data_count.caption" = "Exchanged bytes count";
|
"service.cells.data_count.caption" = "Exchanged data count";
|
||||||
|
"service.cells.data_count.value" = "%@ / %@";
|
||||||
"service.cells.debug_log.caption" = "Debug log";
|
"service.cells.debug_log.caption" = "Debug log";
|
||||||
"service.cells.masks_private_data.caption" = "Mask network data";
|
"service.cells.masks_private_data.caption" = "Mask network data";
|
||||||
"service.cells.report_issue.caption" = "Report connectivity issue";
|
"service.cells.report_issue.caption" = "Report connectivity issue";
|
||||||
|
|
|
@ -79,5 +79,7 @@ public class GroupConstants {
|
||||||
public static let dnsTimeout = 5000
|
public static let dnsTimeout = 5000
|
||||||
|
|
||||||
public static let sessionMarker = "--- EOF ---"
|
public static let sessionMarker = "--- EOF ---"
|
||||||
|
|
||||||
|
public static let dataCountInterval = 5000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,10 @@ public protocol ConnectionServiceDelegate: class {
|
||||||
func connectionService(didActivate profile: ConnectionProfile)
|
func connectionService(didActivate profile: ConnectionProfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension Notification.Name {
|
||||||
|
static let ConnectionServiceDidUpdateDataCount = Notification.Name("ConnectionServiceDidUpdateDataCount")
|
||||||
|
}
|
||||||
|
|
||||||
public class ConnectionService: Codable {
|
public class ConnectionService: Codable {
|
||||||
public enum CodingKeys: String, CodingKey {
|
public enum CodingKeys: String, CodingKey {
|
||||||
case build
|
case build
|
||||||
|
@ -54,6 +58,10 @@ public class ConnectionService: Codable {
|
||||||
|
|
||||||
case preferences
|
case preferences
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct NotificationKeys {
|
||||||
|
public static let dataCount = "DataCount"
|
||||||
|
}
|
||||||
|
|
||||||
public var directory: String? = nil
|
public var directory: String? = nil
|
||||||
|
|
||||||
|
@ -85,6 +93,8 @@ public class ConnectionService: Codable {
|
||||||
|
|
||||||
private var cache: [ProfileKey: ConnectionProfile]
|
private var cache: [ProfileKey: ConnectionProfile]
|
||||||
|
|
||||||
|
private var dataCountObserver: Timer?
|
||||||
|
|
||||||
public private(set) var activeProfileKey: ProfileKey? {
|
public private(set) var activeProfileKey: ProfileKey? {
|
||||||
willSet {
|
willSet {
|
||||||
if let oldProfile = activeProfile {
|
if let oldProfile = activeProfile {
|
||||||
|
@ -130,6 +140,10 @@ public class ConnectionService: Codable {
|
||||||
cache = [:]
|
cache = [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
dataCountObserver?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Codable
|
// MARK: Codable
|
||||||
|
|
||||||
public required init(from decoder: Decoder) throws {
|
public required init(from decoder: Decoder) throws {
|
||||||
|
@ -502,14 +516,6 @@ public class ConnectionService: Codable {
|
||||||
return baseConfiguration.existingLog(in: appGroup) ?? ""
|
return baseConfiguration.existingLog(in: appGroup) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
public var vpnLastError: TunnelKitProvider.ProviderError? {
|
|
||||||
return baseConfiguration.lastError(in: appGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func clearVpnLastError() {
|
|
||||||
baseConfiguration.clearLastError(in: appGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func eraseVpnLog() {
|
public func eraseVpnLog() {
|
||||||
log.info("Erasing VPN log...")
|
log.info("Erasing VPN log...")
|
||||||
guard let url = baseConfiguration.urlForLog(in: appGroup) else {
|
guard let url = baseConfiguration.urlForLog(in: appGroup) else {
|
||||||
|
@ -517,4 +523,26 @@ public class ConnectionService: Codable {
|
||||||
}
|
}
|
||||||
try? FileManager.default.removeItem(at: url)
|
try? FileManager.default.removeItem(at: url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var vpnLastError: TunnelKitProvider.ProviderError? {
|
||||||
|
return baseConfiguration.lastError(in: appGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func clearVpnLastError() {
|
||||||
|
baseConfiguration.clearLastError(in: appGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func observeVPNDataCount(interval: TimeInterval) {
|
||||||
|
dataCountObserver?.invalidate()
|
||||||
|
dataCountObserver = Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: { [weak self] (_) in
|
||||||
|
guard let dataCount = self?.vpnDataCount else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
NotificationCenter.default.post(name: .ConnectionServiceDidUpdateDataCount, object: nil, userInfo: [NotificationKeys.dataCount: dataCount])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public var vpnDataCount: (Int, Int)? {
|
||||||
|
return baseConfiguration.dataCount(in: appGroup)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
//
|
||||||
|
// DataUnit.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 3/30/18.
|
||||||
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum DataUnit: Int, CustomStringConvertible {
|
||||||
|
case byte = 1
|
||||||
|
|
||||||
|
case kilobyte = 1024
|
||||||
|
|
||||||
|
case megabyte = 1048576
|
||||||
|
|
||||||
|
case gigabyte = 1073741824
|
||||||
|
|
||||||
|
fileprivate var showsDecimals: Bool {
|
||||||
|
switch self {
|
||||||
|
case .byte, .kilobyte:
|
||||||
|
return false
|
||||||
|
|
||||||
|
case .megabyte, .gigabyte:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var boundary: Int {
|
||||||
|
return Int(0.1 * Double(rawValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .byte:
|
||||||
|
return "B"
|
||||||
|
|
||||||
|
case .kilobyte:
|
||||||
|
return "kB"
|
||||||
|
|
||||||
|
case .megabyte:
|
||||||
|
return "MB"
|
||||||
|
|
||||||
|
case .gigabyte:
|
||||||
|
return "GB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Int {
|
||||||
|
private static let allUnits: [DataUnit] = [
|
||||||
|
.gigabyte,
|
||||||
|
.megabyte,
|
||||||
|
.kilobyte,
|
||||||
|
.byte
|
||||||
|
]
|
||||||
|
|
||||||
|
var dataUnitDescription: String {
|
||||||
|
if self == 0 {
|
||||||
|
return "0B"
|
||||||
|
}
|
||||||
|
for u in Int.allUnits {
|
||||||
|
if self >= u.boundary {
|
||||||
|
if !u.showsDecimals {
|
||||||
|
return "\(self / u.rawValue)\(u)"
|
||||||
|
}
|
||||||
|
let count = Double(self) / Double(u.rawValue)
|
||||||
|
return String(format: "%.2f%@", count, u.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fatalError("Number is negative")
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,6 +107,7 @@ public class TransientStore {
|
||||||
// _ = service.addProfile(HostConnectionProfile(title: "vps"), credentials: Credentials(username: "foo", password: "bar"))
|
// _ = service.addProfile(HostConnectionProfile(title: "vps"), credentials: Credentials(username: "foo", password: "bar"))
|
||||||
// service.activateProfile(service.profiles.first!)
|
// service.activateProfile(service.profiles.first!)
|
||||||
}
|
}
|
||||||
|
service.observeVPNDataCount(interval: TimeInterval(GroupConstants.VPN.dataCountInterval) / 1000.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serialize(withProfiles: Bool) {
|
public func serialize(withProfiles: Bool) {
|
||||||
|
|
|
@ -541,8 +541,12 @@ public enum L10n {
|
||||||
public static let caption = L10n.tr("Localizable", "service.cells.connection_status.caption")
|
public static let caption = L10n.tr("Localizable", "service.cells.connection_status.caption")
|
||||||
}
|
}
|
||||||
public enum DataCount {
|
public enum DataCount {
|
||||||
/// Exchanged bytes count
|
/// Exchanged data count
|
||||||
public static let caption = L10n.tr("Localizable", "service.cells.data_count.caption")
|
public static let caption = L10n.tr("Localizable", "service.cells.data_count.caption")
|
||||||
|
/// %@ / %@
|
||||||
|
public static func value(_ p1: String, _ p2: String) -> String {
|
||||||
|
return L10n.tr("Localizable", "service.cells.data_count.value", p1, p2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public enum DebugLog {
|
public enum DebugLog {
|
||||||
/// Debug log
|
/// Debug log
|
||||||
|
|
4
Podfile
4
Podfile
|
@ -5,8 +5,8 @@ use_frameworks!
|
||||||
def shared_pods
|
def shared_pods
|
||||||
#pod 'TunnelKit', '~> 1.5.0'
|
#pod 'TunnelKit', '~> 1.5.0'
|
||||||
#pod 'TunnelKit/LZO', '~> 1.5.0'
|
#pod 'TunnelKit/LZO', '~> 1.5.0'
|
||||||
pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'd03f1bd'
|
pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => '44fb5a5'
|
||||||
pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'd03f1bd'
|
pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => '44fb5a5'
|
||||||
#pod 'TunnelKit', :path => '../../personal/tunnelkit'
|
#pod 'TunnelKit', :path => '../../personal/tunnelkit'
|
||||||
#pod 'TunnelKit/LZO', :path => '../../personal/tunnelkit'
|
#pod 'TunnelKit/LZO', :path => '../../personal/tunnelkit'
|
||||||
end
|
end
|
||||||
|
|
10
Podfile.lock
10
Podfile.lock
|
@ -15,8 +15,8 @@ PODS:
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- MBProgressHUD
|
- MBProgressHUD
|
||||||
- TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `d03f1bd`)
|
- TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `44fb5a5`)
|
||||||
- TunnelKit/LZO (from `https://github.com/keeshux/tunnelkit`, commit `d03f1bd`)
|
- TunnelKit/LZO (from `https://github.com/keeshux/tunnelkit`, commit `44fb5a5`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
https://github.com/cocoapods/specs.git:
|
https://github.com/cocoapods/specs.git:
|
||||||
|
@ -26,12 +26,12 @@ SPEC REPOS:
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
TunnelKit:
|
TunnelKit:
|
||||||
:commit: d03f1bd
|
:commit: 44fb5a5
|
||||||
:git: https://github.com/keeshux/tunnelkit
|
:git: https://github.com/keeshux/tunnelkit
|
||||||
|
|
||||||
CHECKOUT OPTIONS:
|
CHECKOUT OPTIONS:
|
||||||
TunnelKit:
|
TunnelKit:
|
||||||
:commit: d03f1bd
|
:commit: 44fb5a5
|
||||||
:git: https://github.com/keeshux/tunnelkit
|
:git: https://github.com/keeshux/tunnelkit
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
|
@ -40,6 +40,6 @@ SPEC CHECKSUMS:
|
||||||
SwiftyBeaver: 8e67ab3cd94389cbbb7a9c7cc02748d98bfee68e
|
SwiftyBeaver: 8e67ab3cd94389cbbb7a9c7cc02748d98bfee68e
|
||||||
TunnelKit: 75323e2f45e698647ccaebd5a6bcae8c002afea4
|
TunnelKit: 75323e2f45e698647ccaebd5a6bcae8c002afea4
|
||||||
|
|
||||||
PODFILE CHECKSUM: a2c0a287a69e21e328e35816f2c813616b80f54c
|
PODFILE CHECKSUM: 871b647058e72fb7ae3c666abb35d2683806455d
|
||||||
|
|
||||||
COCOAPODS: 1.6.1
|
COCOAPODS: 1.6.1
|
||||||
|
|
Loading…
Reference in New Issue