diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 1750afa0..d8e96a50 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 073695782B39DB2B007249BE /* Libcore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 073695722B39DA23007249BE /* Libcore.xcframework */; }; 073695882B3AC757007249BE /* Libcore.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 073695722B39DA23007249BE /* Libcore.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 0736958B2B3AC96D007249BE /* Bundle+Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0736958A2B3AC96D007249BE /* Bundle+Properties.swift */; }; + 0736958D2B3B79E0007249BE /* StatsEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0736958C2B3B79E0007249BE /* StatsEventHandler.swift */; }; 075637BA2B01583F005AFB8E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60F1D4AAC33ACF5C8307310D /* Pods_Runner.framework */; }; 075637BB2B01583F005AFB8E /* Pods_Runner.framework in Embed Frameworks (2 items) */ = {isa = PBXBuildFile; fileRef = 60F1D4AAC33ACF5C8307310D /* Pods_Runner.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 075637BC2B015864005AFB8E /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */; }; @@ -130,6 +131,7 @@ 073695722B39DA23007249BE /* Libcore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Libcore.xcframework; path = Frameworks/Libcore.xcframework; sourceTree = ""; }; 0736957A2B39DE78007249BE /* Libcore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Libcore.xcframework; path = Frameworks/Libcore.xcframework; sourceTree = ""; }; 0736958A2B3AC96D007249BE /* Bundle+Properties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Properties.swift"; sourceTree = ""; }; + 0736958C2B3B79E0007249BE /* StatsEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsEventHandler.swift; sourceTree = ""; }; 075637B22B0157CB005AFB8E /* libcore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libcore.xcframework; path = ../libcore/bin/libcore.xcframework; sourceTree = ""; }; 07A63A832B1E728C00CAFA4D /* Release */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Release; path = ../build/ios/framework/release; sourceTree = ""; }; 07A63A842B1E72AE00CAFA4D /* App.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = App.xcframework; path = ../build/ios/framework/release/App.xcframework; sourceTree = ""; }; @@ -229,6 +231,7 @@ 03B5166A2AE7315E00EA47E2 /* AlertsEventHandler.swift */, 03B5166C2AE7325500EA47E2 /* LogsEventHandler.swift */, 03B5167C2AE7AC6200EA47E2 /* GroupsEventHandler.swift */, + 0736958C2B3B79E0007249BE /* StatsEventHandler.swift */, ); path = Handlers; sourceTree = ""; @@ -674,6 +677,7 @@ 03B516712AE74CCD00EA47E2 /* VPNConfig.swift in Sources */, 03B5166B2AE7315E00EA47E2 /* AlertsEventHandler.swift in Sources */, 0736958B2B3AC96D007249BE /* Bundle+Properties.swift in Sources */, + 0736958D2B3B79E0007249BE /* StatsEventHandler.swift in Sources */, 03B516692AE7306B00EA47E2 /* StatusEventHandler.swift in Sources */, 032158B82ADDF8BF008D943B /* VPNManager.swift in Sources */, 03B516672AE6B93A00EA47E2 /* MethodHandler.swift in Sources */, diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 363905b5..2b3fd695 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -27,6 +27,7 @@ import Libcore AlertsEventHandler.register(with: self.registrar(forPlugin: AlertsEventHandler.name)!) LogsEventHandler.register(with: self.registrar(forPlugin: LogsEventHandler.name)!) GroupsEventHandler.register(with: self.registrar(forPlugin: GroupsEventHandler.name)!) + StatsEventHandler.register(with: self.registrar(forPlugin: StatsEventHandler.name)!) } } diff --git a/ios/Runner/Handlers/GroupsEventHandler.swift b/ios/Runner/Handlers/GroupsEventHandler.swift index cb842ed7..c6179807 100644 --- a/ios/Runner/Handlers/GroupsEventHandler.swift +++ b/ios/Runner/Handlers/GroupsEventHandler.swift @@ -33,8 +33,8 @@ public class GroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler, private var channel: FlutterEventChannel? - var commandClient: LibboxCommandClient? - var events: FlutterEventSink? + private var commandClient: LibboxCommandClient? + private var events: FlutterEventSink? public static func register(with registrar: FlutterPluginRegistrar) { let instance = GroupsEventHandler() diff --git a/ios/Runner/Handlers/StatsEventHandler.swift b/ios/Runner/Handlers/StatsEventHandler.swift index 258e65d3..9430c8e7 100644 --- a/ios/Runner/Handlers/StatsEventHandler.swift +++ b/ios/Runner/Handlers/StatsEventHandler.swift @@ -6,3 +6,65 @@ // import Foundation +import Flutter +import Libcore + +public class StatsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler, LibboxCommandClientHandlerProtocol { + static let name = "\(Bundle.main.serviceIdentifier)/stats" + + private var channel: FlutterEventChannel? + + private var commandClient: LibboxCommandClient? + private var events: FlutterEventSink? + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = StatsEventHandler() + instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger()) + instance.channel?.setStreamHandler(instance) + } + + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) + self.events = events + let opts = LibboxCommandClientOptions() + opts.command = LibboxCommandStatus + opts.statusInterval = 300 + commandClient = LibboxCommandClient(self, options: opts) + try? commandClient?.connect() + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + try? commandClient?.disconnect() + return nil + } + + public func writeStatus(_ message: LibboxStatusMessage?) { + guard + let message + else { return } + let data = [ + "connections-in": message.connectionsIn, + "connections-out": message.connectionsOut, + "uplink": message.uplink, + "downlink": message.downlink, + "uplink-total": message.uplinkTotal, + "downlink-total": message.downlinkTotal + ] as [String:Any] + guard + let json = try? JSONSerialization.data(withJSONObject: data), + let json = String(data: json, encoding: .utf8) + else { return } + events?(json) + } +} + +extension StatsEventHandler { + public func clearLog() {} + public func connected() {} + public func disconnected(_ message: String?) {} + public func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) {} + public func updateClashMode(_ newMode: String?) {} + public func writeGroups(_ message: LibboxOutboundGroupIteratorProtocol?) {} + public func writeLog(_ message: String?) {} +}