From e6ed7977ec896c106739e67a9caa42d8c55ef092 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Thu, 16 Sep 2021 18:56:43 +0200 Subject: [PATCH] feat: added a new option to the Network module which allows to auto reset data usage (#542) --- Modules/Net/main.swift | 42 ++++++++++++++++++---- Modules/Net/settings.swift | 71 +++++++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 35 deletions(-) diff --git a/Modules/Net/main.swift b/Modules/Net/main.swift index ca04e684..b38c536e 100644 --- a/Modules/Net/main.swift +++ b/Modules/Net/main.swift @@ -77,6 +77,7 @@ public class Network: Module { private var processReader: ProcessReader? = nil private let ipUpdater = NSBackgroundActivityScheduler(identifier: "eu.exelban.Stats.Network.IP") + private let usageReseter = NSBackgroundActivityScheduler(identifier: "eu.exelban.Stats.Network.Usage") public init() { self.settingsView = Settings("Network") @@ -115,6 +116,9 @@ public class Network: Module { self.usageReader?.getDetails() self.usageReader?.read() } + self.settingsView.usageResetCallback = { [unowned self] in + self.setUsageReset() + } if let reader = self.usageReader { self.addReader(reader) @@ -123,13 +127,8 @@ public class Network: Module { self.addReader(reader) } - self.ipUpdater.interval = 60 * 60 - self.ipUpdater.repeats = true - self.ipUpdater.schedule { (completion: @escaping NSBackgroundActivityScheduler.CompletionHandler) in - debug("going to automatically refresh IP address...") - NotificationCenter.default.post(name: .refreshPublicIP, object: nil, userInfo: nil) - completion(NSBackgroundActivityScheduler.Result.finished) - } + self.setIPUpdater() + self.setUsageReset() } public override func isAvailable() -> Bool { @@ -157,4 +156,33 @@ public class Network: Module { } } } + + private func setIPUpdater() { + self.ipUpdater.interval = 60 * 60 + self.ipUpdater.repeats = true + self.ipUpdater.schedule { (completion: @escaping NSBackgroundActivityScheduler.CompletionHandler) in + debug("going to automatically refresh IP address...") + NotificationCenter.default.post(name: .refreshPublicIP, object: nil, userInfo: nil) + completion(NSBackgroundActivityScheduler.Result.finished) + } + } + + private func setUsageReset() { + self.usageReseter.invalidate() + + switch AppUpdateInterval(rawValue: Store.shared.string(key: "\(self.config.name)_usageReset", defaultValue: AppUpdateInterval.atStart.rawValue)) { + case .oncePerDay: self.usageReseter.interval = 60 * 60 * 24 + case .oncePerWeek: self.usageReseter.interval = 60 * 60 * 24 * 7 + case .oncePerMonth: self.usageReseter.interval = 60 * 60 * 24 * 30 + case .never, .atStart: return + default: return + } + + self.usageReseter.repeats = true + self.usageReseter.schedule { (completion: @escaping NSBackgroundActivityScheduler.CompletionHandler) in + debug("going to reset the usage...") + NotificationCenter.default.post(name: .resetTotalNetworkUsage, object: nil, userInfo: nil) + completion(NSBackgroundActivityScheduler.Result.finished) + } + } } diff --git a/Modules/Net/settings.swift b/Modules/Net/settings.swift index 17456cff..580c92cb 100644 --- a/Modules/Net/settings.swift +++ b/Modules/Net/settings.swift @@ -13,12 +13,14 @@ import Cocoa import Kit import SystemConfiguration -internal class Settings: NSView, Settings_v { +internal class Settings: NSStackView, Settings_v { private var numberOfProcesses: Int = 8 private var readerType: String = "interface" + private var usageReset: String = AppUpdateInterval.atStart.rawValue public var callback: (() -> Void) = {} public var callbackWhenUpdateNumberOfProcesses: (() -> Void) = {} + public var usageResetCallback: (() -> Void) = {} private let title: String private var button: NSPopUpButton? @@ -29,13 +31,9 @@ internal class Settings: NSView, Settings_v { self.title = title self.numberOfProcesses = Store.shared.int(key: "\(self.title)_processes", defaultValue: self.numberOfProcesses) self.readerType = Store.shared.string(key: "\(self.title)_reader", defaultValue: self.readerType) + self.usageReset = Store.shared.string(key: "\(self.title)_usageReset", defaultValue: self.usageReset) - super.init(frame: CGRect( - x: 0, - y: 0, - width: Constants.Settings.width - (Constants.Settings.margin*2), - height: 0 - )) + super.init(frame: NSRect(x: 0, y: 0, width: Constants.Settings.width - (Constants.Settings.margin*2), height: 0)) for interface in SCNetworkInterfaceCopyAll() as NSArray { if let bsdName = SCNetworkInterfaceGetBSDName(interface as! SCNetworkInterface), @@ -44,7 +42,15 @@ internal class Settings: NSView, Settings_v { } } - self.canDrawConcurrently = true + self.orientation = .vertical + self.distribution = .gravityAreas + self.edgeInsets = NSEdgeInsets( + top: Constants.Settings.margin, + left: Constants.Settings.margin, + bottom: Constants.Settings.margin, + right: Constants.Settings.margin + ) + self.spacing = Constants.Settings.margin } required init?(coder: NSCoder) { @@ -54,48 +60,48 @@ internal class Settings: NSView, Settings_v { public func load(widgets: [widget_t]) { self.subviews.forEach{ $0.removeFromSuperview() } - let rowHeight: CGFloat = 30 - let num: CGFloat = 2 + let width: CGFloat = self.frame.width - (Constants.Settings.margin*2) - self.addSubview(selectTitleRow( - frame: NSRect( - x: Constants.Settings.margin, - y: Constants.Settings.margin + (rowHeight + Constants.Settings.margin) * 2, - width: self.frame.width - (Constants.Settings.margin*2), - height: 30 - ), + self.addArrangedSubview(selectTitleRow( + frame: NSRect(x: 0, y: 0, width: width, height: Constants.Settings.row), title: localizedString("Number of top processes"), action: #selector(changeNumberOfProcesses), items: NumbersOfProcesses.map{ "\($0)" }, selected: "\(self.numberOfProcesses)" )) - self.addSubview(selectRow( - frame: NSRect( - x: Constants.Settings.margin, - y: Constants.Settings.margin + (rowHeight + Constants.Settings.margin) * 1, - width: self.frame.width - (Constants.Settings.margin*2), - height: 30 - ), + self.addArrangedSubview(selectRow( + frame: NSRect(x: 0, y: 0, width: width, height: Constants.Settings.row), title: localizedString("Reader type"), action: #selector(changeReaderType), items: NetworkReaders, selected: self.readerType )) + self.addArrangedSubview(selectRow( + frame: NSRect(x: 0, y: 0, width: width, height: Constants.Settings.row), + title: localizedString("Reset data usage"), + action: #selector(toggleUsageReset), + items: AppUpdateIntervals.dropLast(2), + selected: self.usageReset + )) + self.addInterfaceSelector() - self.setFrameSize(NSSize(width: self.frame.width, height: (rowHeight*(num+1)) + (Constants.Settings.margin*(2+num)))) + let h = self.arrangedSubviews.map({ $0.bounds.height + self.spacing }).reduce(0, +) - self.spacing + self.edgeInsets.top + self.edgeInsets.bottom + if self.frame.size.height != h { + self.setFrameSize(NSSize(width: self.bounds.width, height: h)) + } } private func addInterfaceSelector() { - let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: self.frame.width, height: 30)) + let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 30)) let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: (view.frame.height - 16)/2, width: view.frame.width - 52, height: 17), localizedString("Network interface")) rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light) rowTitle.textColor = .textColor - self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 200 - Constants.Settings.margin*2, y: 0, width: 200, height: 30)) + self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 200 - Constants.Settings.margin*2, y: 0, width: 200, height: 26)) self.button?.target = self self.button?.action = #selector(self.handleSelection) self.button?.isEnabled = self.readerType == "interface" @@ -124,7 +130,7 @@ internal class Settings: NSView, Settings_v { view.addSubview(rowTitle) view.addSubview(self.button!) - self.addSubview(view) + self.addArrangedSubview(view) } @objc func handleSelection(_ sender: NSPopUpButton) { @@ -157,4 +163,13 @@ internal class Settings: NSView, Settings_v { Store.shared.set(key: "\(self.title)_reader", value: key) self.button?.isEnabled = self.readerType == "interface" } + + @objc private func toggleUsageReset(_ sender: NSMenuItem) { + guard let key = sender.representedObject as? String else { + return + } + self.usageReset = key + Store.shared.set(key: "\(self.title)_usageReset", value: key) + self.usageResetCallback() + } }