Add source code
This commit is contained in:
parent
69c08de01c
commit
0416d157d7
14 changed files with 1672 additions and 0 deletions
183
TetheringHider/TetheringHiderApp.swift
Normal file
183
TetheringHider/TetheringHiderApp.swift
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
// TetheringHiderApp.swift
|
||||
//
|
||||
//
|
||||
// Created by Jesús David Chapman Vélez on 12/08/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
@MainActor
|
||||
final class StatusBarController: NSObject, NSMenuDelegate {
|
||||
private var statusItem: NSStatusItem?
|
||||
private let ttlManager: TTLManager
|
||||
private let authService = AuthenticationService()
|
||||
private let commandService = SystemCommandService()
|
||||
|
||||
init(ttlManager: TTLManager) {
|
||||
self.ttlManager = ttlManager
|
||||
super.init()
|
||||
setupStatusItem()
|
||||
|
||||
self.ttlManager.onStatusChanged = { [weak self] in
|
||||
Task { @MainActor in
|
||||
self?.updateMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupStatusItem() {
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
updateMenu()
|
||||
}
|
||||
|
||||
func updateMenu() {
|
||||
guard let statusItem = statusItem, let button = statusItem.button else { return }
|
||||
|
||||
// Status Bar Icon
|
||||
let imageName = ttlManager.isProtected
|
||||
? "antenna.radiowaves.left.and.right"
|
||||
: "antenna.radiowaves.left.and.right.slash"
|
||||
|
||||
button.image = NSImage(systemSymbolName: imageName, accessibilityDescription: "Tethering Hider")
|
||||
|
||||
let menu = NSMenu()
|
||||
menu.delegate = self
|
||||
|
||||
let statusTitle = ttlManager.isProtected
|
||||
? String(localized: "Tethering Hider — Hotspot Cloaked")
|
||||
: String(localized: "Tethering Hider — Standard Tethering")
|
||||
|
||||
let headerItem = NSMenuItem(title: statusTitle, action: nil, keyEquivalent: "")
|
||||
headerItem.isEnabled = false
|
||||
headerItem.image = NSImage(
|
||||
systemSymbolName: ttlManager.isProtected ? "eye.slash.fill" : "eye.fill",
|
||||
accessibilityDescription: nil
|
||||
)
|
||||
menu.addItem(headerItem)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let toggleTitle = ttlManager.isProtected
|
||||
? String(localized: "Disable Hotspot Cloak (Set TTL 64)")
|
||||
: String(localized: "Enable Hotspot Cloak (Set TTL 65)")
|
||||
|
||||
let toggleItem = NSMenuItem(title: toggleTitle, action: #selector(toggleAction), keyEquivalent: "")
|
||||
toggleItem.target = self
|
||||
toggleItem.image = NSImage(
|
||||
systemSymbolName: ttlManager.isProtected ? "eye.fill" : "eye.slash.fill",
|
||||
accessibilityDescription: nil
|
||||
)
|
||||
menu.addItem(toggleItem)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let ipv4Format = String(localized: "IPv4 TTL: %d")
|
||||
let ipv4Item = NSMenuItem(title: String(format: ipv4Format, ttlManager.currentTTL), action: nil, keyEquivalent: "")
|
||||
ipv4Item.isEnabled = false
|
||||
ipv4Item.image = NSImage(systemSymbolName: "network", accessibilityDescription: nil)
|
||||
menu.addItem(ipv4Item)
|
||||
|
||||
let ipv6Format = String(localized: "IPv6 HLIM: %d")
|
||||
let ipv6Item = NSMenuItem(title: String(format: ipv6Format, ttlManager.currentIPv6HopLimit), action: nil, keyEquivalent: "")
|
||||
ipv6Item.isEnabled = false
|
||||
ipv6Item.image = NSImage(systemSymbolName: "globe", accessibilityDescription: nil)
|
||||
menu.addItem(ipv6Item)
|
||||
|
||||
let dnsTitle = String(localized: "DNS Flush: AUTO")
|
||||
let dnsItem = NSMenuItem(title: dnsTitle, action: nil, keyEquivalent: "")
|
||||
dnsItem.isEnabled = false
|
||||
dnsItem.image = NSImage(systemSymbolName: "arrow.triangle.2.circlepath", accessibilityDescription: nil)
|
||||
menu.addItem(dnsItem)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let aboutTitle = String(localized: "About Tethering Hider...")
|
||||
let aboutItem = NSMenuItem(title: aboutTitle, action: #selector(aboutAction), keyEquivalent: "")
|
||||
aboutItem.target = self
|
||||
aboutItem.image = NSImage(systemSymbolName: "info.circle.fill", accessibilityDescription: nil)
|
||||
menu.addItem(aboutItem)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let quitTitle = String(localized: "Quit Tethering Hider")
|
||||
let quitItem = NSMenuItem(title: quitTitle, action: #selector(quitAction), keyEquivalent: "q")
|
||||
quitItem.target = self
|
||||
quitItem.image = NSImage(systemSymbolName: "rectangle.portrait.and.arrow.right", accessibilityDescription: nil)
|
||||
menu.addItem(quitItem)
|
||||
|
||||
statusItem.menu = menu
|
||||
}
|
||||
|
||||
func menuWillOpen(_ menu: NSMenu) {
|
||||
ttlManager.refreshStatus()
|
||||
}
|
||||
|
||||
@objc private func toggleAction() {
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let authenticated = try await authService.authenticateWithBiometrics()
|
||||
guard authenticated else { return }
|
||||
|
||||
if ttlManager.isProtected {
|
||||
try await commandService.resetTTL()
|
||||
ttlManager.didSetTTL(ttlManager.defaultTTL)
|
||||
} else {
|
||||
try await commandService.setTTL(ttlManager.targetTTL)
|
||||
ttlManager.didSetTTL(ttlManager.targetTTL)
|
||||
}
|
||||
updateMenu()
|
||||
} catch {
|
||||
ttlManager.setError(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func aboutAction() {
|
||||
let creditsString = String(localized: "Bypasses mobile carrier hotspot tethering detection by adjusting outgoing IPv4 TTL to 65, IPv6 Hop Limit to 65, and flushing system DNS caches.\n\nDesigned for macOS.")
|
||||
let options: [NSApplication.AboutPanelOptionKey: Any] = [
|
||||
.credits: NSAttributedString(
|
||||
string: creditsString,
|
||||
attributes: [
|
||||
.font: NSFont.systemFont(ofSize: 11),
|
||||
.foregroundColor: NSColor.secondaryLabelColor
|
||||
]
|
||||
),
|
||||
.applicationName: "Tethering Hider",
|
||||
.version: "1.0.0"
|
||||
]
|
||||
NSApp.orderFrontStandardAboutPanel(options: options)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
@objc private func quitAction() {
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
var statusBarController: StatusBarController?
|
||||
let ttlManager = TTLManager()
|
||||
|
||||
func applicationWillFinishLaunching(_ notification: Notification) {
|
||||
NSApp.setActivationPolicy(.accessory)
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
NSApp.setActivationPolicy(.accessory)
|
||||
self.statusBarController = StatusBarController(ttlManager: ttlManager)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct TetheringHiderApp: App {
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
|
||||
var body: some Scene {
|
||||
Settings {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue