Add ios connection info
This commit is contained in:
@@ -42,6 +42,9 @@
|
|||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
D703EE932B764EA3001D88B3 /* CommandClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D703EE922B764EA3001D88B3 /* CommandClient.swift */; };
|
||||||
|
D703EE962B765176001D88B3 /* ActiveGroupsEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D703EE952B765176001D88B3 /* ActiveGroupsEventHandler.swift */; };
|
||||||
|
D7CC50862B768C50006BC140 /* Outbound.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC50852B768C50006BC140 /* Outbound.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -164,6 +167,9 @@
|
|||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
9AC67B4DCF829F5B6F63AA7D /* Pods-Runner-SingBoxPacketTunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SingBoxPacketTunnel.release.xcconfig"; path = "Target Support Files/Pods-Runner-SingBoxPacketTunnel/Pods-Runner-SingBoxPacketTunnel.release.xcconfig"; sourceTree = "<group>"; };
|
9AC67B4DCF829F5B6F63AA7D /* Pods-Runner-SingBoxPacketTunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SingBoxPacketTunnel.release.xcconfig"; path = "Target Support Files/Pods-Runner-SingBoxPacketTunnel/Pods-Runner-SingBoxPacketTunnel.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
C20A211B58CE31B2738D133C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
C20A211B58CE31B2738D133C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
D703EE922B764EA3001D88B3 /* CommandClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandClient.swift; sourceTree = "<group>"; };
|
||||||
|
D703EE952B765176001D88B3 /* ActiveGroupsEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveGroupsEventHandler.swift; sourceTree = "<group>"; };
|
||||||
|
D7CC50852B768C50006BC140 /* Outbound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Outbound.swift; sourceTree = "<group>"; };
|
||||||
F3FFE1D9C2D5629FACC123EE /* Pods-Runner-SingBoxPacketTunnel.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SingBoxPacketTunnel.profile.xcconfig"; path = "Target Support Files/Pods-Runner-SingBoxPacketTunnel/Pods-Runner-SingBoxPacketTunnel.profile.xcconfig"; sourceTree = "<group>"; };
|
F3FFE1D9C2D5629FACC123EE /* Pods-Runner-SingBoxPacketTunnel.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SingBoxPacketTunnel.profile.xcconfig"; path = "Target Support Files/Pods-Runner-SingBoxPacketTunnel/Pods-Runner-SingBoxPacketTunnel.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -218,6 +224,7 @@
|
|||||||
03B5166C2AE7325500EA47E2 /* LogsEventHandler.swift */,
|
03B5166C2AE7325500EA47E2 /* LogsEventHandler.swift */,
|
||||||
03B5167C2AE7AC6200EA47E2 /* GroupsEventHandler.swift */,
|
03B5167C2AE7AC6200EA47E2 /* GroupsEventHandler.swift */,
|
||||||
0736958C2B3B79E0007249BE /* StatsEventHandler.swift */,
|
0736958C2B3B79E0007249BE /* StatsEventHandler.swift */,
|
||||||
|
D703EE952B765176001D88B3 /* ActiveGroupsEventHandler.swift */,
|
||||||
);
|
);
|
||||||
path = Handlers;
|
path = Handlers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -258,6 +265,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
03E392CE2ADDEFC8000ADF15 /* FilePath.swift */,
|
03E392CE2ADDEFC8000ADF15 /* FilePath.swift */,
|
||||||
|
D703EE922B764EA3001D88B3 /* CommandClient.swift */,
|
||||||
|
D7CC50852B768C50006BC140 /* Outbound.swift */,
|
||||||
);
|
);
|
||||||
path = Shared;
|
path = Shared;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -672,13 +681,16 @@
|
|||||||
03B5167B2AE79DB400EA47E2 /* FileMethodHandler.swift in Sources */,
|
03B5167B2AE79DB400EA47E2 /* FileMethodHandler.swift in Sources */,
|
||||||
03B516772AE7634400EA47E2 /* Logger.swift in Sources */,
|
03B516772AE7634400EA47E2 /* Logger.swift in Sources */,
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
|
D703EE962B765176001D88B3 /* ActiveGroupsEventHandler.swift in Sources */,
|
||||||
03B516712AE74CCD00EA47E2 /* VPNConfig.swift in Sources */,
|
03B516712AE74CCD00EA47E2 /* VPNConfig.swift in Sources */,
|
||||||
03B5166B2AE7315E00EA47E2 /* AlertsEventHandler.swift in Sources */,
|
03B5166B2AE7315E00EA47E2 /* AlertsEventHandler.swift in Sources */,
|
||||||
0736958B2B3AC96D007249BE /* Bundle+Properties.swift in Sources */,
|
0736958B2B3AC96D007249BE /* Bundle+Properties.swift in Sources */,
|
||||||
0736958D2B3B79E0007249BE /* StatsEventHandler.swift in Sources */,
|
0736958D2B3B79E0007249BE /* StatsEventHandler.swift in Sources */,
|
||||||
03B516692AE7306B00EA47E2 /* StatusEventHandler.swift in Sources */,
|
03B516692AE7306B00EA47E2 /* StatusEventHandler.swift in Sources */,
|
||||||
|
D7CC50862B768C50006BC140 /* Outbound.swift in Sources */,
|
||||||
032158B82ADDF8BF008D943B /* VPNManager.swift in Sources */,
|
032158B82ADDF8BF008D943B /* VPNManager.swift in Sources */,
|
||||||
0736958F2B3B8048007249BE /* PlatformMethodHandler.swift in Sources */,
|
0736958F2B3B8048007249BE /* PlatformMethodHandler.swift in Sources */,
|
||||||
|
D703EE932B764EA3001D88B3 /* CommandClient.swift in Sources */,
|
||||||
03B516672AE6B93A00EA47E2 /* MethodHandler.swift in Sources */,
|
03B516672AE6B93A00EA47E2 /* MethodHandler.swift in Sources */,
|
||||||
03B5166D2AE7325500EA47E2 /* LogsEventHandler.swift in Sources */,
|
03B5166D2AE7325500EA47E2 /* LogsEventHandler.swift in Sources */,
|
||||||
03E392D02ADDF1BD000ADF15 /* FilePath.swift in Sources */,
|
03E392D02ADDF1BD000ADF15 /* FilePath.swift in Sources */,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import Libcore
|
|||||||
AlertsEventHandler.register(with: self.registrar(forPlugin: AlertsEventHandler.name)!)
|
AlertsEventHandler.register(with: self.registrar(forPlugin: AlertsEventHandler.name)!)
|
||||||
LogsEventHandler.register(with: self.registrar(forPlugin: LogsEventHandler.name)!)
|
LogsEventHandler.register(with: self.registrar(forPlugin: LogsEventHandler.name)!)
|
||||||
GroupsEventHandler.register(with: self.registrar(forPlugin: GroupsEventHandler.name)!)
|
GroupsEventHandler.register(with: self.registrar(forPlugin: GroupsEventHandler.name)!)
|
||||||
|
ActiveGroupsEventHandler.register(with: self.registrar(forPlugin: ActiveGroupsEventHandler.name)!)
|
||||||
StatsEventHandler.register(with: self.registrar(forPlugin: StatsEventHandler.name)!)
|
StatsEventHandler.register(with: self.registrar(forPlugin: StatsEventHandler.name)!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
ios/Runner/Handlers/ActiveGroupsEventHandler.swift
Normal file
49
ios/Runner/Handlers/ActiveGroupsEventHandler.swift
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import Libcore
|
||||||
|
|
||||||
|
public class ActiveGroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler {
|
||||||
|
|
||||||
|
static let name = "\(Bundle.main.serviceIdentifier)/active-groups"
|
||||||
|
private var commandClient: CommandClient?
|
||||||
|
private var channel: FlutterEventChannel?
|
||||||
|
private var events: FlutterEventSink?
|
||||||
|
private var cancellable: AnyCancellable?
|
||||||
|
|
||||||
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
|
let instance = ActiveGroupsEventHandler()
|
||||||
|
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
|
||||||
|
commandClient = CommandClient(.groupsInfoOnly)
|
||||||
|
commandClient?.connect()
|
||||||
|
cancellable = commandClient?.$groups.sink{ [self] sbGroups in
|
||||||
|
self.writeGroups(sbGroups)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||||
|
commandClient?.disconnect()
|
||||||
|
cancellable?.cancel()
|
||||||
|
events = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeGroups(_ sbGroups: [SBGroup]?) {
|
||||||
|
guard let sbGroups else {return}
|
||||||
|
if
|
||||||
|
let groups = try? JSONEncoder().encode(sbGroups),
|
||||||
|
let groups = String(data: groups, encoding: .utf8)
|
||||||
|
{
|
||||||
|
DispatchQueue.main.async { [events = self.events, groups] () in
|
||||||
|
events?(groups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,93 +1,50 @@
|
|||||||
//
|
|
||||||
// GroupsEventHandler.swift
|
|
||||||
// Runner
|
|
||||||
//
|
|
||||||
// Created by GFWFighter on 10/24/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Combine
|
||||||
import Libcore
|
import Libcore
|
||||||
|
|
||||||
struct SBItem: Codable {
|
public class GroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler{
|
||||||
let tag: String
|
|
||||||
let type: String
|
|
||||||
let urlTestDelay: Int
|
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
|
||||||
case tag
|
|
||||||
case type
|
|
||||||
case urlTestDelay = "url-test-delay"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SBGroup: Codable {
|
|
||||||
let tag: String
|
|
||||||
let type: String
|
|
||||||
let selected: String
|
|
||||||
let items: [SBItem]
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler, LibboxCommandClientHandlerProtocol {
|
|
||||||
|
|
||||||
static let name = "\(Bundle.main.serviceIdentifier)/groups"
|
static let name = "\(Bundle.main.serviceIdentifier)/groups"
|
||||||
|
|
||||||
|
private var commandClient: CommandClient?
|
||||||
private var channel: FlutterEventChannel?
|
private var channel: FlutterEventChannel?
|
||||||
|
|
||||||
private var commandClient: LibboxCommandClient?
|
|
||||||
private var events: FlutterEventSink?
|
private var events: FlutterEventSink?
|
||||||
|
private var cancellable: AnyCancellable?
|
||||||
|
|
||||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
let instance = GroupsEventHandler()
|
let instance = GroupsEventHandler()
|
||||||
instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger())
|
instance.channel = FlutterEventChannel(name: Self.name,
|
||||||
|
binaryMessenger: registrar.messenger())
|
||||||
instance.channel?.setStreamHandler(instance)
|
instance.channel?.setStreamHandler(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
||||||
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
|
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
|
||||||
self.events = events
|
self.events = events
|
||||||
let opts = LibboxCommandClientOptions()
|
commandClient = CommandClient(.groups)
|
||||||
opts.command = LibboxCommandGroup
|
commandClient?.connect()
|
||||||
opts.statusInterval = 3000
|
cancellable = commandClient?.$groups.sink{ [self] groups in
|
||||||
commandClient = LibboxCommandClient(self, options: opts)
|
self.writeGroups(groups)
|
||||||
try? commandClient?.connect()
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||||
try? commandClient?.disconnect()
|
commandClient?.disconnect()
|
||||||
|
cancellable?.cancel()
|
||||||
|
events = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func writeGroups(_ message: LibboxOutboundGroupIteratorProtocol?) {
|
func writeGroups(_ sbGroups: [SBGroup]?) {
|
||||||
guard let message else { return }
|
guard let sbGroups else {return}
|
||||||
var groups = [SBGroup]()
|
|
||||||
while message.hasNext() {
|
|
||||||
let group = message.next()!
|
|
||||||
var items = [SBItem]()
|
|
||||||
var groupItems = group.getItems()
|
|
||||||
while groupItems?.hasNext() ?? false {
|
|
||||||
let item = groupItems?.next()!
|
|
||||||
items.append(SBItem(tag: item!.tag, type: item!.type, urlTestDelay: Int(item!.urlTestDelay)))
|
|
||||||
}
|
|
||||||
groups.append(.init(tag: group.tag, type: group.type, selected: group.selected, items: items))
|
|
||||||
}
|
|
||||||
if
|
if
|
||||||
let groups = try? JSONEncoder().encode(groups),
|
let groups = try? JSONEncoder().encode(sbGroups),
|
||||||
let groups = String(data: groups, encoding: .utf8)
|
let groups = String(data: groups, encoding: .utf8)
|
||||||
{
|
{
|
||||||
DispatchQueue.main.async { [events = self.events, groups] () in
|
DispatchQueue.main.async { [events = self.events, groups] in
|
||||||
events?(groups)
|
events?(groups)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GroupsEventHandler {
|
|
||||||
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 writeLog(_ message: String?) {}
|
|
||||||
public func writeStatus(_ message: LibboxStatusMessage?) {}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,48 +1,46 @@
|
|||||||
//
|
|
||||||
// StatsEventHandler.swift
|
|
||||||
// Runner
|
|
||||||
//
|
|
||||||
// Created by Hiddify on 12/27/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Flutter
|
import Flutter
|
||||||
|
import Combine
|
||||||
import Libcore
|
import Libcore
|
||||||
|
|
||||||
public class StatsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler, LibboxCommandClientHandlerProtocol {
|
public class StatsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler {
|
||||||
|
|
||||||
static let name = "\(Bundle.main.serviceIdentifier)/stats"
|
static let name = "\(Bundle.main.serviceIdentifier)/stats"
|
||||||
|
|
||||||
|
private var commandClient: CommandClient?
|
||||||
private var channel: FlutterEventChannel?
|
private var channel: FlutterEventChannel?
|
||||||
|
|
||||||
private var commandClient: LibboxCommandClient?
|
|
||||||
private var events: FlutterEventSink?
|
private var events: FlutterEventSink?
|
||||||
|
private var cancellable: AnyCancellable?
|
||||||
|
|
||||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
let instance = StatsEventHandler()
|
let instance = StatsEventHandler()
|
||||||
instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger(), codec: FlutterJSONMethodCodec())
|
instance.channel = FlutterEventChannel(name: Self.name,
|
||||||
|
binaryMessenger: registrar.messenger(),
|
||||||
|
codec: FlutterJSONMethodCodec())
|
||||||
instance.channel?.setStreamHandler(instance)
|
instance.channel?.setStreamHandler(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
||||||
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
|
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
|
||||||
self.events = events
|
self.events = events
|
||||||
let opts = LibboxCommandClientOptions()
|
commandClient = CommandClient(.status)
|
||||||
opts.command = LibboxCommandStatus
|
commandClient?.connect()
|
||||||
opts.statusInterval = Int64(NSEC_PER_SEC)
|
cancellable = commandClient?.$status.sink{ [self] status in
|
||||||
commandClient = LibboxCommandClient(self, options: opts)
|
self.writeStatus(status)
|
||||||
try? commandClient?.connect()
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||||
try? commandClient?.disconnect()
|
commandClient?.disconnect()
|
||||||
|
cancellable?.cancel()
|
||||||
|
events = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func writeStatus(_ message: LibboxStatusMessage?) {
|
func writeStatus(_ message: LibboxStatusMessage?) {
|
||||||
guard
|
guard let message else { return }
|
||||||
let message
|
|
||||||
else { return }
|
|
||||||
let data = [
|
let data = [
|
||||||
"connections-in": message.connectionsIn,
|
"connections-in": message.connectionsIn,
|
||||||
"connections-out": message.connectionsOut,
|
"connections-out": message.connectionsOut,
|
||||||
@@ -54,13 +52,3 @@ public class StatsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler, L
|
|||||||
events?(data)
|
events?(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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?) {}
|
|
||||||
}
|
|
||||||
|
|||||||
166
ios/Shared/CommandClient.swift
Normal file
166
ios/Shared/CommandClient.swift
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import Foundation
|
||||||
|
import Libcore
|
||||||
|
|
||||||
|
public class CommandClient: ObservableObject {
|
||||||
|
public enum ConnectionType {
|
||||||
|
case status
|
||||||
|
case groups
|
||||||
|
case log
|
||||||
|
case groupsInfoOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
private let connectionType: ConnectionType
|
||||||
|
private let logMaxLines: Int
|
||||||
|
private var commandClient: LibboxCommandClient?
|
||||||
|
private var connectTask: Task<Void, Error>?
|
||||||
|
|
||||||
|
@Published private(set) var isConnected: Bool
|
||||||
|
@Published private(set) var status: LibboxStatusMessage?
|
||||||
|
@Published private(set) var groups: [SBGroup]?
|
||||||
|
@Published private(set) var logList: [String]
|
||||||
|
|
||||||
|
public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) {
|
||||||
|
self.connectionType = connectionType
|
||||||
|
self.logMaxLines = logMaxLines
|
||||||
|
logList = []
|
||||||
|
isConnected = false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func connect() {
|
||||||
|
if isConnected {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let connectTask {
|
||||||
|
connectTask.cancel()
|
||||||
|
}
|
||||||
|
connectTask = Task {
|
||||||
|
await connect0()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func disconnect() {
|
||||||
|
if let connectTask {
|
||||||
|
connectTask.cancel()
|
||||||
|
self.connectTask = nil
|
||||||
|
}
|
||||||
|
if let commandClient {
|
||||||
|
try? commandClient.disconnect()
|
||||||
|
self.commandClient = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private nonisolated func connect0() async {
|
||||||
|
let clientOptions = LibboxCommandClientOptions()
|
||||||
|
switch connectionType {
|
||||||
|
case .status:
|
||||||
|
clientOptions.command = LibboxCommandStatus
|
||||||
|
case .groups:
|
||||||
|
clientOptions.command = LibboxCommandGroup
|
||||||
|
case .log:
|
||||||
|
clientOptions.command = LibboxCommandLog
|
||||||
|
case .groupsInfoOnly:
|
||||||
|
clientOptions.command = LibboxCommandGroupInfoOnly
|
||||||
|
}
|
||||||
|
clientOptions.statusInterval = Int64(2 * NSEC_PER_SEC)
|
||||||
|
let client = LibboxNewCommandClient(clientHandler(self), clientOptions)!
|
||||||
|
do {
|
||||||
|
for i in 0 ..< 10 {
|
||||||
|
try await Task.sleep(nanoseconds: UInt64(Double(100 + (i * 50)) * Double(NSEC_PER_MSEC)))
|
||||||
|
try Task.checkCancellation()
|
||||||
|
do {
|
||||||
|
try client.connect()
|
||||||
|
await MainActor.run {
|
||||||
|
commandClient = client
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} catch {}
|
||||||
|
try Task.checkCancellation()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
try? client.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class clientHandler: NSObject, LibboxCommandClientHandlerProtocol {
|
||||||
|
private let commandClient: CommandClient
|
||||||
|
|
||||||
|
init(_ commandClient: CommandClient) {
|
||||||
|
self.commandClient = commandClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func connected() {
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
if commandClient.connectionType == .log {
|
||||||
|
commandClient.logList = []
|
||||||
|
}
|
||||||
|
commandClient.isConnected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnected(_: String?) {
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
commandClient.isConnected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearLog() {
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
commandClient.logList.removeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLog(_ message: String?) {
|
||||||
|
guard let message else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
if commandClient.logList.count > commandClient.logMaxLines {
|
||||||
|
commandClient.logList.removeFirst()
|
||||||
|
}
|
||||||
|
commandClient.logList.append(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeStatus(_ message: LibboxStatusMessage?) {
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
commandClient.status = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeGroups(_ groups: LibboxOutboundGroupIteratorProtocol?) {
|
||||||
|
guard let groups else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var sbGroups = [SBGroup]()
|
||||||
|
while groups.hasNext() {
|
||||||
|
let group = groups.next()!
|
||||||
|
var items = [SBItem]()
|
||||||
|
let groupItems = group.getItems()
|
||||||
|
while groupItems?.hasNext() ?? false {
|
||||||
|
let item = groupItems?.next()!
|
||||||
|
items.append(SBItem(tag: item!.tag,
|
||||||
|
type: item!.type,
|
||||||
|
urlTestDelay: Int(item!.urlTestDelay)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sbGroups.append(.init(tag: group.tag,
|
||||||
|
type: group.type,
|
||||||
|
selected: group.selected,
|
||||||
|
items: items)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
commandClient.groups = sbGroups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateClashMode(_ newMode: String?) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
ios/Shared/Outbound.swift
Normal file
18
ios/Shared/Outbound.swift
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
public struct SBItem: Codable {
|
||||||
|
let tag: String
|
||||||
|
let type: String
|
||||||
|
let urlTestDelay: Int
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case tag
|
||||||
|
case type
|
||||||
|
case urlTestDelay = "url-test-delay"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SBGroup: Codable {
|
||||||
|
let tag: String
|
||||||
|
let type: String
|
||||||
|
let selected: String
|
||||||
|
let items: [SBItem]
|
||||||
|
}
|
||||||
@@ -62,7 +62,8 @@ class HomePage extends HookConsumerWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Expanded(child: ConnectionButton()),
|
const Expanded(child: ConnectionButton()),
|
||||||
if (Platform.isAndroid) const ActiveProxyFooter(),
|
if (Platform.isAndroid || Platform.isIOS)
|
||||||
|
const ActiveProxyFooter(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user