Added global Keyboard shortcut

So we could switch between different proxy modes with global shortcut.
This commit is contained in:
Vicent Tsai
2016-12-11 15:39:56 +08:00
parent cc342df5b5
commit 23967eca20
10 changed files with 236 additions and 20 deletions

View File

@ -57,6 +57,8 @@ So after you quit the app, the ss-local maybe be still running.
Added a manual mode which won't configure the system proxy settings. Added a manual mode which won't configure the system proxy settings.
Then you could configure your apps to use socks5 proxy manual. Then you could configure your apps to use socks5 proxy manual.
Added global Keyboard shortcut <kbd></kbd> + <kbd></kbd> + <kbd>P</kbd> to switch between `global` mode and `auto` mode.
## Contributing ## Contributing
Contributions must be available on a separately named branch based on the latest version of the main branch develop. Contributions must be available on a separately named branch based on the latest version of the main branch develop.

View File

@ -427,7 +427,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0730; LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730; LastUpgradeCheck = 0810;
ORGANIZATIONNAME = qiuyuzhou; ORGANIZATIONNAME = qiuyuzhou;
TargetAttributes = { TargetAttributes = {
9B0BFFE41D0460A70040E62B = { 9B0BFFE41D0460A70040E62B = {
@ -764,6 +764,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@ -774,8 +775,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_IDENTITY = "Mac Developer";
@ -809,6 +812,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@ -819,8 +823,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_IDENTITY = "Mac Developer";
@ -839,6 +845,7 @@
MACOSX_DEPLOYMENT_TARGET = 10.11; MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
}; };
name = Release; name = Release;
}; };
@ -846,6 +853,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = FE3237E9FB24D9B924A0E630 /* Pods-ShadowsocksX-NG.debug.xcconfig */; baseConfigurationReference = FE3237E9FB24D9B924A0E630 /* Pods-ShadowsocksX-NG.debug.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
@ -873,6 +881,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = E9E9FB3855DA55D0710EE7BD /* Pods-ShadowsocksX-NG.release.xcconfig */; baseConfigurationReference = E9E9FB3855DA55D0710EE7BD /* Pods-ShadowsocksX-NG.release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0730" LastUpgradeVersion = "0810"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0730" LastUpgradeVersion = "0810"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0730" LastUpgradeVersion = "0810"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -7,7 +7,7 @@
// //
import Cocoa import Cocoa
import Carbon
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate { class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
@ -19,6 +19,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
var editUserRulesWinCtrl: UserRulesController! var editUserRulesWinCtrl: UserRulesController!
var httpPreferencesWinCtrl : HTTPPreferencesWindowController! var httpPreferencesWinCtrl : HTTPPreferencesWindowController!
let keyCode = kVK_ANSI_P
let modifierKeys = cmdKey+controlKey
var hotKeyRef: EventHotKeyRef?
var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController() var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController()
@IBOutlet weak var window: NSWindow! @IBOutlet weak var window: NSWindow!
@ -39,6 +43,22 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
@IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem! @IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem!
@IBOutlet weak var hudWindow: NSPanel!
@IBOutlet weak var panelView: NSView!
@IBOutlet weak var isNameTextField: NSTextField!
let kHudFadeInDuration: Double = 0.25
let kHudFadeOutDuration: Double = 0.5
let kHudDisplayDuration: Double = 2.0
let kHudAlphaValue: CGFloat = 0.75
let kHudCornerRadius: CGFloat = 18.0
let kHudHorizontalMargin: CGFloat = 30
let kHudHeight: CGFloat = 90.0
var timerToFadeOut: Timer? = nil
var fadingOut: Bool = false
var statusItem: NSStatusItem! var statusItem: NSStatusItem!
static let StatusItemIconWidth:CGFloat = 20 static let StatusItemIconWidth:CGFloat = 20
@ -135,8 +155,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
userNote.subtitle = "By Handle SS URL".localized userNote.subtitle = "By Handle SS URL".localized
} }
userNote.informativeText = "Host: \(profile.serverHost)" userNote.informativeText = "Host: \(profile.serverHost)"
" Port: \(profile.serverPort)" //" Port: \(profile.serverPort)"
" Encription Method: \(profile.method)".localized //" Encription Method: \(profile.method)".localized
userNote.soundName = NSUserNotificationDefaultSoundName userNote.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.default NSUserNotificationCenter.default
@ -165,6 +185,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
ProxyConfHelper.startMonitorPAC() ProxyConfHelper.startMonitorPAC()
applyConfig() applyConfig()
SyncSSLocal() SyncSSLocal()
// Register global hotkey
registerHotkey()
} }
func applicationWillTerminate(_ aNotification: Notification) { func applicationWillTerminate(_ aNotification: Notification) {
@ -172,6 +195,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
StopSSLocal() StopSSLocal()
StopPrivoxy() StopPrivoxy()
ProxyConfHelper.disableProxy() ProxyConfHelper.disableProxy()
if let ref = hotKeyRef { UnregisterEventHotKey(ref) }
} }
func applyConfig() { func applyConfig() {
@ -196,6 +220,63 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
} }
} }
// MARK: - Hotkey Methods
func registerHotkey() -> Void {
var gMyHotKeyID = EventHotKeyID()
gMyHotKeyID.signature = OSType(fourCharCodeFrom(string: "sxng"))
gMyHotKeyID.id = UInt32(keyCode)
var eventType = EventTypeSpec()
eventType.eventClass = OSType(kEventClassKeyboard)
eventType.eventKind = OSType(kEventHotKeyPressed)
// Void pointer to `self`:
let context = Unmanaged.passUnretained(self).toOpaque()
// Install handler.
InstallEventHandler(GetApplicationEventTarget(), {(nextHanlder, theEvent, userContext) -> OSStatus in
// Extract pointer to `self` from void pointer:
let mySelf = Unmanaged<AppDelegate>.fromOpaque(userContext!).takeUnretainedValue()
switch Globals.proxyType {
case .pac:
Globals.proxyType = .global
UserDefaults.standard.setValue("global", forKey: "ShadowsocksRunningMode")
mySelf.isNameTextField.stringValue = "Gobal Mode"
mySelf.updateRunningModeMenu()
mySelf.applyConfig()
case .global:
Globals.proxyType = .pac
UserDefaults.standard.setValue("auto", forKey: "ShadowsocksRunningMode")
mySelf.isNameTextField.stringValue = "Auto Mode"
mySelf.updateRunningModeMenu()
mySelf.applyConfig()
}
mySelf.fadeInHud()
return noErr
}, 1, &eventType, context, nil)
// Register hotkey.
RegisterEventHotKey(UInt32(keyCode),
UInt32(modifierKeys),
gMyHotKeyID,
GetApplicationEventTarget(),
0,
&hotKeyRef)
}
func fourCharCodeFrom(string: String) -> FourCharCode {
assert(string.characters.count == 4, "String length must be 4")
var result: FourCharCode = 0
for char in string.utf16 {
result = (result << 8) + FourCharCode(char)
}
return result
}
// MARK: - UI Methods
@IBAction func toggleRunning(_ sender: NSMenuItem) { @IBAction func toggleRunning(_ sender: NSMenuItem) {
let defaults = UserDefaults.standard let defaults = UserDefaults.standard
var isOn = defaults.bool(forKey: "ShadowsocksOn") var isOn = defaults.bool(forKey: "ShadowsocksOn")
@ -510,3 +591,93 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
} }
} }
extension AppDelegate {
func fadeInHud() -> Void {
if timerToFadeOut != nil {
timerToFadeOut?.invalidate()
timerToFadeOut = nil
}
fadingOut = false
hudWindow.orderFrontRegardless()
CATransaction.begin()
CATransaction.setAnimationDuration(kHudFadeInDuration)
CATransaction.setCompletionBlock { self.didFadeIn() }
panelView.layer?.opacity = 1.0
CATransaction.commit()
}
func didFadeIn() -> Void {
timerToFadeOut = Timer.scheduledTimer(
timeInterval: kHudDisplayDuration,
target: self,
selector: #selector(fadeOutHud),
userInfo: nil,
repeats: false)
}
func fadeOutHud() -> Void {
fadingOut = true
CATransaction.begin()
CATransaction.setAnimationDuration(kHudFadeOutDuration)
CATransaction.setCompletionBlock { self.didFadeOut() }
panelView.layer?.opacity = 0.0
CATransaction.commit()
}
func didFadeOut() -> Void {
if fadingOut {
self.hudWindow.orderOut(nil)
}
fadingOut = false
}
func setupHud() -> Void {
isNameTextField.stringValue = "Global Mode"
isNameTextField.sizeToFit()
var labelFrame: CGRect = isNameTextField.frame
var hudWindowFrame: CGRect = hudWindow.frame
hudWindowFrame.size.width = labelFrame.size.width + kHudHorizontalMargin * 2
hudWindowFrame.size.height = kHudHeight
let screenRect: NSRect = NSScreen.screens()![0].visibleFrame
hudWindowFrame.origin.x = (screenRect.size.width - hudWindowFrame.size.width) / 2
hudWindowFrame.origin.y = (screenRect.size.height - hudWindowFrame.size.height) / 2
hudWindow.setFrame(hudWindowFrame, display: true)
var viewFrame: NSRect = hudWindowFrame;
viewFrame.origin.x = 0
viewFrame.origin.y = 0
panelView.frame = viewFrame
labelFrame.origin.x = kHudHorizontalMargin
labelFrame.origin.y = (hudWindowFrame.size.height - labelFrame.size.height) / 2
isNameTextField.frame = labelFrame
}
func initUIComponent() -> Void {
hudWindow.isOpaque = false
hudWindow.backgroundColor = .clear
hudWindow.level = Int(CGWindowLevelForKey(.utilityWindow)) + 1000
hudWindow.styleMask = .borderless
hudWindow.hidesOnDeactivate = false
hudWindow.collectionBehavior = .canJoinAllSpaces
let viewLayer: CALayer = CALayer()
viewLayer.backgroundColor = CGColor.init(red: 0.05, green: 0.05, blue: 0.05, alpha: kHudAlphaValue)
viewLayer.cornerRadius = kHudCornerRadius
panelView.wantsLayer = true
panelView.layer = viewLayer
panelView.layer?.opacity = 0.0
setupHud()
}
override func awakeFromNib() {
initUIComponent()
}
}

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="16A323" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"> <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -14,11 +15,13 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="ShadowsocksX_NG" customModuleProvider="target"> <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="ShadowsocksX_NG" customModuleProvider="target">
<connections> <connections>
<outlet property="RunningStatusMenuItem" destination="fzk-mE-CEV" id="R4x-gK-Qcw"/>
<outlet property="autoModeMenuItem" destination="r07-Gu-aEz" id="9aH-pQ-Rgi"/> <outlet property="autoModeMenuItem" destination="r07-Gu-aEz" id="9aH-pQ-Rgi"/>
<outlet property="globalModeMenuItem" destination="Mw3-Jm-eXA" id="ar5-Yx-3ze"/> <outlet property="globalModeMenuItem" destination="Mw3-Jm-eXA" id="ar5-Yx-3ze"/>
<outlet property="hudWindow" destination="QWV-F6-ac1" id="K6D-a4-oqE"/>
<outlet property="isNameTextField" destination="rUN-Nq-HDb" id="ayK-aM-rSa"/>
<outlet property="lanchAtLoginMenuItem" destination="eUq-p7-ICK" id="w4p-0c-DZn"/> <outlet property="lanchAtLoginMenuItem" destination="eUq-p7-ICK" id="w4p-0c-DZn"/>
<outlet property="manualModeMenuItem" destination="8PR-gs-c5N" id="9qz-mU-5kt"/> <outlet property="manualModeMenuItem" destination="8PR-gs-c5N" id="9qz-mU-5kt"/>
<outlet property="panelView" destination="rai-SH-9tZ" id="5aU-ld-rWb"/>
<outlet property="proxyMenuItem" destination="diI-fB-Rss" id="Qjk-9U-3Qy"/> <outlet property="proxyMenuItem" destination="diI-fB-Rss" id="Qjk-9U-3Qy"/>
<outlet property="runningStatusMenuItem" destination="fzk-mE-CEV" id="Vwm-Rg-Ykn"/> <outlet property="runningStatusMenuItem" destination="fzk-mE-CEV" id="Vwm-Rg-Ykn"/>
<outlet property="scanQRCodeMenuItem" destination="Qe6-bF-paT" id="XHa-pa-nCa"/> <outlet property="scanQRCodeMenuItem" destination="Qe6-bF-paT" id="XHa-pa-nCa"/>
@ -164,7 +167,7 @@
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
<point key="canvasLocation" x="22" y="89"/> <point key="canvasLocation" x="-2367" y="-139"/>
</menu> </menu>
<menu id="2oY-e9-q1Z"> <menu id="2oY-e9-q1Z">
<items> <items>
@ -178,6 +181,30 @@
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
</menuItem> </menuItem>
</items> </items>
<point key="canvasLocation" x="-2113" y="-132"/>
</menu> </menu>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="QWV-F6-ac1" customClass="NSPanel">
<windowStyleMask key="styleMask" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<rect key="contentRect" x="139" y="81" width="200" height="100"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<value key="minSize" type="size" width="200" height="100"/>
<value key="maxSize" type="size" width="200" height="100"/>
<view key="contentView" id="rai-SH-9tZ" userLabel="Panel View">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="162" translatesAutoresizingMaskIntoConstraints="NO" id="rUN-Nq-HDb" userLabel="Is Name">
<rect key="frame" x="17" y="33" width="166" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" title="Label" usesSingleLineMode="YES" id="jcH-j9-Xl3">
<font key="font" metaFont="system" size="24"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" name="alternateSelectedControlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<point key="canvasLocation" x="-2357" y="172"/>
</window>
</objects> </objects>
</document> </document>

View File

@ -51,7 +51,7 @@ class ServerProfileManager: NSObject {
if activeProfileId != nil { if activeProfileId != nil {
defaults.set(activeProfileId, forKey: "ActiveServerProfileId") defaults.set(activeProfileId, forKey: "ActiveServerProfileId")
writeSSLocalConfFile((getActiveProfile()?.toJsonConfig())!) _ = writeSSLocalConfFile((getActiveProfile()?.toJsonConfig())!)
} else { } else {
defaults.removeObject(forKey: "ActiveServerProfileId") defaults.removeObject(forKey: "ActiveServerProfileId")
removeSSLocalConfFile() removeSSLocalConfFile()

View File

@ -8,14 +8,12 @@
import Foundation import Foundation
extension String { extension String {
var localized: String { var localized: String {
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
} }
} }
extension Data { extension Data {
func sha1() -> String { func sha1() -> String {
let data = self let data = self
@ -25,3 +23,12 @@ extension Data {
return hexBytes.joined(separator: "") return hexBytes.joined(separator: "")
} }
} }
enum ProxyType {
case pac
case global
}
struct Globals {
static var proxyType = ProxyType.pac
}

View File

@ -44,9 +44,9 @@
"Turn Shadowsocks On" = "打开 Shadowsocks"; "Turn Shadowsocks On" = "打开 Shadowsocks";
"Proxy - Auto By PAC" = "代理 - PAC自动"; "Proxy - Auto By PAC" = "代理 - PAC自动⌃⌘P";
"Proxy - Global" = "代理 - 全局"; "Proxy - Global" = "代理 - 全局⌃⌘P";
"Proxy - Manual" = "代理 - 手动"; "Proxy - Manual" = "代理 - 手动";