Skip to content

Commit

Permalink
Merge branch 'meta-dev' into meta
Browse files Browse the repository at this point in the history
  • Loading branch information
mrFq1 committed Nov 2, 2023
2 parents 68b950c + 6e66c63 commit 727c4e7
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 25 deletions.
6 changes: 6 additions & 0 deletions ClashX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
0106179F2AF38EFA005C7877 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49FEC6682AD9369C00BAD9F5 /* Command.swift */; };
015B976A2A4F2F4500F9FA4D /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97692A4F2F4500F9FA4D /* Alamofire */; };
015B976D2A4F2F6C00F9FA4D /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 015B976C2A4F2F6C00F9FA4D /* RxCocoa */; };
015B976F2A4F2F6C00F9FA4D /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 015B976E2A4F2F6C00F9FA4D /* RxSwift */; };
Expand Down Expand Up @@ -221,6 +222,7 @@
49D223392A1DA5F10002FFCB /* SSIDSuspendTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */; };
49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45129AEEC15006487EF /* StatusItemTool.swift */; };
49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */; };
49FEC6692AD9369C00BAD9F5 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49FEC6682AD9369C00BAD9F5 /* Command.swift */; };
8A2BBEA727A03ACB0081EBEF /* ProxySetting.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */; };
8ACD21BB27A04C7800BC4632 /* ProxySettingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */; };
8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BC27A04ED500BC4632 /* ProxyModeChangeCommand.swift */; };
Expand Down Expand Up @@ -399,6 +401,7 @@
49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemViewProtocol.swift; sourceTree = "<group>"; };
49D8276627E9B01700159D93 /* LoginKitWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginKitWrapper.h; sourceTree = "<group>"; };
49D8276727E9B01700159D93 /* LoginKitWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginKitWrapper.m; sourceTree = "<group>"; };
49FEC6682AD9369C00BAD9F5 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = ProxySetting.sdef; sourceTree = "<group>"; };
8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxySettingCommand.swift; sourceTree = "<group>"; };
8ACD21BC27A04ED500BC4632 /* ProxyModeChangeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyModeChangeCommand.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -515,6 +518,7 @@
49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */,
49B445152457CDF000B27E3E /* ClashStatusTool.swift */,
49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */,
49FEC6682AD9369C00BAD9F5 /* Command.swift */,
);
path = Utils;
sourceTree = "<group>";
Expand Down Expand Up @@ -1019,6 +1023,7 @@
01F335E82AD10D0B0048AF77 /* AlphaMetaDownloader.swift in Sources */,
01F335E92AD10D0B0048AF77 /* NetworkChangeNotifier.swift in Sources */,
01F335EA2AD10D0B0048AF77 /* GlobalShortCutViewController.swift in Sources */,
0106179F2AF38EFA005C7877 /* Command.swift in Sources */,
01F335EB2AD10D0B0048AF77 /* Notification.swift in Sources */,
01F335EC2AD10D0B0048AF77 /* ClashMetaConfig.swift in Sources */,
01F335ED2AD10D0B0048AF77 /* ProxyModeChangeCommand.swift in Sources */,
Expand Down Expand Up @@ -1135,6 +1140,7 @@
49722FF0211F338B00650A41 /* EventStream.swift in Sources */,
499A486522EEA3FD00F6C675 /* Array+Safe.swift in Sources */,
F92D0B24236BC12000575E15 /* SavedProxyModel.swift in Sources */,
49FEC6692AD9369C00BAD9F5 /* Command.swift in Sources */,
F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */,
49D223392A1DA5F10002FFCB /* SSIDSuspendTool.swift in Sources */,
F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions ClashX/Basic/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class Logger {
}
}

static func log(_ msg: String, level: ClashLogLevel = .info, function: String = #function) {
shared.logToFile(msg: "[\(level.rawValue)] \(function) \(msg)", level: level)
static func log(_ msg: String, level: ClashLogLevel = .info, file: String = #file, function: String = #function) {
shared.logToFile(msg: "[\(level.rawValue)] \(file) \(function) \(msg)", level: level)
}

func logFilePath() -> String {
Expand Down
2 changes: 2 additions & 0 deletions ClashX/ClashWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class ClashWindowController<T: NSViewController>: NSWindowController, NSWindowDe
}
window?.makeKeyAndOrderFront(self)
window?.delegate = self
NSApp.activate(ignoringOtherApps: true)
window?.makeKeyAndOrderFront(nil)
}

func windowWillClose(_ notification: Notification) {
Expand Down
34 changes: 34 additions & 0 deletions ClashX/General/Utils/Command.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Command.swift
// ClashX
//
// Created by yicheng on 2023/10/13.
// Copyright © 2023 west2online. All rights reserved.
//

import Foundation

struct Command {
let cmd: String
let args: [String]

func run() -> String {
var output = ""

let task = Process()
task.launchPath = cmd
task.arguments = args

let outpipe = Pipe()
task.standardOutput = outpipe

task.launch()

task.waitUntilExit()
let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: outdata, encoding: .utf8) {
output = string.trimmingCharacters(in: .newlines)
}
return output
}
}
4 changes: 0 additions & 4 deletions ClashX/General/Utils/NetworkChangeNotifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,4 @@ class NetworkChangeNotifier {
}
return allowIPV6 ? ipv6 : nil
}

static func getCurrentSSID() -> String? {
return CWWiFiClient.shared().interface()?.ssid()
}
}
113 changes: 100 additions & 13 deletions ClashX/General/Utils/SSIDSuspendTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,48 @@
// Copyright © 2023 west2online. All rights reserved.
//

import CoreLocation
import CoreWLAN
import Foundation
import RxCocoa
import RxSwift
import AppKit

class SSIDSuspendTool {
class SSIDSuspendTool: NSObject {
static let shared = SSIDSuspendTool()
var disposeBag = DisposeBag()
func setup() {
NotificationCenter
.default
.rx
.notification(.systemNetworkStatusDidChange)
.observe(on: MainScheduler.instance)
.delay(.seconds(2), scheduler: MainScheduler.instance)
.bind { [weak self] _ in
self?.update()
}.disposed(by: disposeBag)
private var ssidChangePublisher = PublishSubject<String>()
private var disposeBag = DisposeBag()
private lazy var locationManager = CLLocationManager()

var showNoticeOnNotPermission = false

func setup() {
if AppVersionUtil.hasVersionChanged {
showNoticeOnNotPermission = true
}
requestPermissionIfNeed()
do {
try CWWiFiClient.shared().startMonitoringEvent(with: .ssidDidChange)
CWWiFiClient.shared().delegate = self
ssidChangePublisher
.observe(on: MainScheduler.instance)
.debounce(.seconds(1), scheduler: MainScheduler.instance)
.delay(.seconds(1), scheduler: MainScheduler.instance)
.bind { [weak self] _ in
self?.update()
}.disposed(by: disposeBag)
} catch let err {
Logger.log(String(describing: err), level: .warning)
NotificationCenter
.default
.rx
.notification(.systemNetworkStatusDidChange)
.observe(on: MainScheduler.instance)
.delay(.seconds(2), scheduler: MainScheduler.instance)
.bind { [weak self] _ in
self?.update()
}.disposed(by: disposeBag)
}
ConfigManager.shared
.proxyShouldPaused
.asObservable()
Expand All @@ -40,6 +64,27 @@ class SSIDSuspendTool {
update()
}

func requestPermissionIfNeed() {
defer {
showNoticeOnNotPermission = false
}
if #available(macOS 14, *) {
if Settings.disableSSIDList.isEmpty { return }
if locationManager.authorizationStatus == .notDetermined {
Logger.log("request location permission")
locationManager.desiredAccuracy = kCLLocationAccuracyReduced
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
} else if locationManager.authorizationStatus != .authorized {
if showNoticeOnNotPermission {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.openLocationSettings()
}
}
}
}
}

func update() {
if shouldSuspend() {
ConfigManager.shared.proxyShouldPaused.accept(true)
Expand All @@ -49,10 +94,52 @@ class SSIDSuspendTool {
}

func shouldSuspend() -> Bool {
if let currentSSID = NetworkChangeNotifier.getCurrentSSID() {
if let currentSSID = getCurrentSSID() {
return Settings.disableSSIDList.contains(currentSSID)
} else {
return false
}
}

private func getCurrentSSID() -> String? {
if #available(macOS 14, *) {
if locationManager.authorizationStatus != .authorized {
let info = Command(cmd: "/System/Library/PrivateFrameworks/Apple80211.framework/Resources/airport", args: ["-I"]).run()
let ssid = info.components(separatedBy: "\n")
.lazy
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.first { $0.starts(with: "SSID:") }?
.components(separatedBy: ":")
.last?.trimmingCharacters(in: .whitespacesAndNewlines)
return ssid
}
}
return CWWiFiClient.shared().interface()?.ssid()
}

private func openLocationSettings() {
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Location")!)
NSApp.activate(ignoringOtherApps: true)
NSAlert.alert(with: NSLocalizedString("Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services.", comment: ""))
}
}

extension SSIDSuspendTool: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
Logger.log("Location status: \(status.rawValue)")
if status != .authorized, showNoticeOnNotPermission {
openLocationSettings()
}
showNoticeOnNotPermission = false
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {}
}

extension SSIDSuspendTool: CWEventDelegate {
func ssidDidChangeForWiFiInterface(withName interfaceName: String) {
ssidChangePublisher.onNext(interfaceName)
}
}
4 changes: 4 additions & 0 deletions ClashX/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>ClashX use location info to detect your current WiFi network SSID name and provide the auto suspend services.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>ClashX use location info to detect your current WiFi network SSID name and provide the auto suspend services.</string>
<key>BETA</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
Expand Down
8 changes: 2 additions & 6 deletions ClashX/Models/ClashProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,8 @@ class ClashProxy: Codable {
var proxys = [SpeedtestAbleItem]()
for proxy in allProxys {
if let p = resp.proxiesMap[proxy] {
if !ClashProxyType.isProxyGroup(p) {
if let provider = p.enclosingProvider {
proxys.append(.provider(name: p.name, provider: provider.name))
} else {
proxys.append(.proxy(name: p.name))
}
if let provider = p.enclosingProvider {
proxys.append(.provider(name: p.name, provider: provider.name))
} else {
proxys.append(.group(name: p.name))
}
Expand Down
3 changes: 3 additions & 0 deletions ClashX/Support Files/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@
/* No comment provided by engineer. */
"Open github release page to download " = "Open github release page to download ";

/* No comment provided by engineer. */
"Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services." = "Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services.";

/* No comment provided by engineer. */
"Ports Open Fail, Please try to restart ClashX" = "Ports Open Fail, Please try to restart ClashX";

Expand Down
3 changes: 3 additions & 0 deletions ClashX/Support Files/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@
/* No comment provided by engineer. */
"Open github release page to download " = "打开 GitHub Release 下载 ";

/* No comment provided by engineer. */
"Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services." = "请允许ClashX使用定位服务来获取当前所连接的WiFi名称从而提供按需暂停服务。";

/* No comment provided by engineer. */
"Ports Open Fail, Please try to restart ClashX" = "端口打开失败,请尝试重启ClashX";

Expand Down
3 changes: 3 additions & 0 deletions ClashX/Support Files/zh-Hant.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@
/* No comment provided by engineer. */
"Open System Login Item Setting" = "打開系統登錄項設定";

/* No comment provided by engineer. */
"Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services." = "請允許 ClashX 使用定位服務,以獲取您目前連接的 WiFi 名稱,並提供按需暫停服務。";

/* No comment provided by engineer. */
"Ports Open Fail, Please try to restart ClashX" = "端口打開失敗,請嘗試重啟ClashX";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ class GeneralSettingViewController: NSViewController {
if url.isUrlVaild() || url.isEmpty {
Settings.benchMarkUrl = url
}
SSIDSuspendTool.shared.showNoticeOnNotPermission = true
SSIDSuspendTool.shared.requestPermissionIfNeed()
SSIDSuspendTool.shared.update()
}

@IBAction func actionResetIgnoreList(_ sender: Any) {
Expand Down

0 comments on commit 727c4e7

Please sign in to comment.