diff options
| author | FivePixels <o5pxels@gmail.com> | 2019-01-03 15:19:08 -0600 |
|---|---|---|
| committer | FivePixels <o5pxels@gmail.com> | 2019-01-03 15:19:08 -0600 |
| commit | d77e14b07f5afb81c0b1dfc6911e209d63e17d99 (patch) | |
| tree | f657ac6e66032b0003ce90723eacc5e315b829a2 | |
| parent | a491f672c4dc979dfd9165bc043b8ca2e984f5f7 (diff) | |
| download | dwa140shortcut-d77e14b07f5afb81c0b1dfc6911e209d63e17d99.tar.xz dwa140shortcut-d77e14b07f5afb81c0b1dfc6911e209d63e17d99.zip | |
Basically a rewrite to clean up the code. Adds some notifications if things happen, and adds support (hopefully?) for LaunchAtLogin. Oh yeah, and the menubar icons are actually clear now - not blurry :)
12 files changed, 176 insertions, 136 deletions
diff --git a/DWA140Menu.xcodeproj/project.pbxproj b/DWA140Menu.xcodeproj/project.pbxproj index 6729fa4..9fb929d 100644 --- a/DWA140Menu.xcodeproj/project.pbxproj +++ b/DWA140Menu.xcodeproj/project.pbxproj @@ -96,6 +96,13 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + E7D7967C21DE824F0004055A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = "<group>"; + }; E7DA7E3D213DD9DD00931016 = { isa = PBXGroup; children = ( @@ -103,6 +110,7 @@ E7DA7E5B213DD9DD00931016 /* DWA140MenuTests */, E7DA7E66213DD9DD00931016 /* DWA140MenuUITests */, E7DA7E47213DD9DD00931016 /* Products */, + E7D7967C21DE824F0004055A /* Frameworks */, ); sourceTree = "<group>"; }; diff --git a/DWA140Menu/AppDelegate.swift b/DWA140Menu/AppDelegate.swift index 150103b..155ed9a 100644 --- a/DWA140Menu/AppDelegate.swift +++ b/DWA140Menu/AppDelegate.swift @@ -8,118 +8,47 @@ import Cocoa import Foundation +import Defaults +import LaunchAtLogin +import SystemConfiguration -// lines 14 - 96 https://stackoverflow.com/a/39175667 - -struct Networking { - - enum NetworkInterfaceType: String, CustomStringConvertible { - case Ethernet = "en2" - case Unknown = "" - var description: String { - switch self { - case .Ethernet: - return "Ethernet" - case .Unknown: - return "Unknown" - } - } - } - static var networkInterfaceType: NetworkInterfaceType { - if let name = Networking().getInterfaces().first?.name, let type = NetworkInterfaceType(rawValue: name) { - return type - } - return .Unknown - } - static var isConnectedByEthernet: Bool { - let networking = Networking() - for addr in networking.getInterfaces() { - if addr.name == NetworkInterfaceType.Ethernet.rawValue { - return true - } - } - return false - } - - func getInterfaces() -> [(name : String, addr: String, mac : String)] { - var addresses = [(name : String, addr: String, mac : String)]() - var nameToMac = [ String: String ]() - // Get list of all interfaces on the local machine: - var ifaddr : UnsafeMutablePointer<ifaddrs>? - guard getifaddrs(&ifaddr) == 0 else { return [] } - guard let firstAddr = ifaddr else { return [] } - // For each interface ... - for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) { - let flags = Int32(ptr.pointee.ifa_flags) - if var addr = ptr.pointee.ifa_addr { - let name = String(cString: ptr.pointee.ifa_name) - // Check for running IPv4, IPv6 interfaces. Skip the loopback interface. - if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) { - switch Int32(addr.pointee.sa_family) { - case AF_LINK: - nameToMac[name] = withUnsafePointer(to: &addr) { unsafeAddr in - unsafeAddr.withMemoryRebound(to: sockaddr_dl.self, capacity: 1) { dl in - dl.withMemoryRebound(to: Int8.self, capacity: 1) { dll in - let lladdr = UnsafeRawBufferPointer(start: dll + 8 + Int(dl.pointee.sdl_nlen), count: Int(dl.pointee.sdl_alen)) - if lladdr.count == 6 { - return lladdr.map { String(format:"%02hhx", $0)}.joined(separator: ":") - } else { - return nil - } - } - } - } - case AF_INET, AF_INET6: - // Convert interface address to a human readable string: - var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) - if (getnameinfo(addr, socklen_t(addr.pointee.sa_len), - &hostname, socklen_t(hostname.count), - nil, socklen_t(0), NI_NUMERICHOST) == 0) { - let address = String(cString: hostname) - addresses.append( (name: name, addr: address, mac : "") ) - } - default: - break - } - } - } - } - freeifaddrs(ifaddr) - // Now add the mac address to the tuples: - for (i, addr) in addresses.enumerated() { - if let mac = nameToMac[addr.name] { - addresses[i] = (name: addr.name, addr: addr.addr, mac : mac) - } - } - return addresses - } +extension Defaults.Keys { + static let launchAtLogin = Defaults.Key<Bool>("launchAtLogin", default: false) } + +let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) +var launchAtLoginMenuItem = NSMenuItem() +var interfaceName : String = "" +let dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, "iked" as CFString, nil, nil) +let ipv4key = SCDynamicStoreCopyValue(dynRef, "State:/Network/Global/IPv4" as CFString) +var isDeviceInterfaceConnected : Bool? +var deviceIPAddress : String = "" + @NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate { - let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) - @objc func changeIcon() { - // change the icon based on the current state (only called on application launch) - if let button = statusItem.button { - if Networking.isConnectedByEthernet { - button.image = NSImage(named: "WifiConnected") - } else { - button.image = NSImage(named: "WifiError") - } - } - } - @objc func openCredits() { +class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate { + + @objc func openSourcePage() { NSWorkspace.shared.open((URL(string:"https://github.com/fivepixels/dwa140shortcut") ?? nil)!) } - @objc func ethStatus() -> String { - // check if the device is connected by Ethernet - if Networking.isConnectedByEthernet { - return "USB WiFi is connected." + + @objc func openDWAPreferencePane() { + let fileManager = FileManager.default + let allUsersPath = "/Library/PreferencePanes/DWA-140WirelessUtility.prefpane" + let currentUserPath = "~/Library/PreferencePanes/DWA-140WirelessUtility.prefPane" + if fileManager.fileExists(atPath: allUsersPath) { + NSWorkspace.shared.open(URL(fileURLWithPath: allUsersPath)) } + else if fileManager.fileExists(atPath: currentUserPath) { + NSWorkspace.shared.open(URL(fileURLWithPath: currentUserPath)) } else { - return "USB WiFi is disconnected." + showNotification(title: "DWA-140 Shortcut Error", withText: "The preference pane does not exist.") } } - @objc func refreshStatus() { - // recreate a new instance of the application + + @objc func openNetworkPreferencePane() { + NSWorkspace.shared.open(URL(fileURLWithPath: "/System/Library/PreferencePanes/Network.prefPane")) + } + + @objc func refreshApplication() { let url = URL(fileURLWithPath: Bundle.main.resourcePath!) let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString let task = Process() @@ -128,42 +57,94 @@ class AppDelegate: NSObject, NSApplicationDelegate { task.launch() exit(0) } - @objc func openDWA() { - // opens the DWA menu *if it exists* on the computer. - let fileman = FileManager.default - let alluserspath = "/Library/PreferencePanes/DWA-140WirelessUtility.prefPane" - let currentuserpath = "~/Library/PreferencePanes/DWA-140WirelessUtility.prefPane" - if fileman.fileExists(atPath: alluserspath) { - NSWorkspace.shared.open(URL(fileURLWithPath: alluserspath)) } - else if fileman.fileExists(atPath: currentuserpath) { - NSWorkspace.shared.open(URL(fileURLWithPath: currentuserpath)) + + @objc func toggleLaunchAtLogin() { + let launchAtLogin = defaults[.launchAtLogin] + launchAtLoginMenuItem.state = !launchAtLogin ? .on : .off + LaunchAtLogin.isEnabled = !launchAtLogin + defaults[.launchAtLogin] = !launchAtLogin + if !launchAtLogin { + showNotification(title: "DWA-140 Shortcut", withText: "This application now opens as soon as you login. 😀") + } + } + + @objc func showNotification(title: String, withText: String) -> Void { + let notification = NSUserNotification() + notification.title = title + notification.informativeText = withText + notification.soundName = NSUserNotificationDefaultSoundName + NSUserNotificationCenter.default.delegate = self + NSUserNotificationCenter.default.deliver(notification) + } + + func getInterface() -> String { + // thanks NetUtils-Swift :p + if var dict1 = ipv4key as? [String: AnyObject] { + if (dict1["PrimaryInterface"] == nil) { + interfaceName = "none" + } + interfaceName = (dict1["PrimaryInterface"] as! String) + } + return interfaceName + } + + func constructMenu() { + let applicationMenu = NSMenu() + applicationMenu.addItem(NSMenuItem(title: "DWA-140 Shortcut (Source)", action: #selector(openSourcePage), keyEquivalent: "")) + launchAtLoginMenuItem = NSMenuItem(title: "Launch Application on Login", action: #selector(toggleLaunchAtLogin), keyEquivalent: "") + launchAtLoginMenuItem.state = defaults[.launchAtLogin] ? .on : .off + applicationMenu.addItem(launchAtLoginMenuItem) + applicationMenu.addItem(NSMenuItem.separator()) + applicationMenu.addItem(NSMenuItem(title: isInterfaceConnected(), action: nil, keyEquivalent: "")) + applicationMenu.addItem(NSMenuItem.separator()) + applicationMenu.addItem(NSMenuItem(title: "Refresh Status", action: #selector(refreshApplication), keyEquivalent: "r")) + applicationMenu.addItem(NSMenuItem(title: "Open DWA-140", action: #selector(openDWAPreferencePane), keyEquivalent: "t")) + applicationMenu.addItem(NSMenuItem(title: "Network Preferences", action: #selector(openNetworkPreferencePane), keyEquivalent: "")) + applicationMenu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) + statusItem.menu = applicationMenu + + } + + func isInterfaceConnected() -> String { + if getInterface() == "en0" { + isDeviceInterfaceConnected = true + DispatchQueue.main.async { + self.setStatusIcon(icon: "WiFiConnected") + } + return "USB WiFi is connected. [Interface: en0]" + } else if getInterface() == "en1" { + isDeviceInterfaceConnected = true + DispatchQueue.main.async { + self.setStatusIcon(icon: "WiFiConnected") + } + return "USB WiFi is connected. [Interface: en1]" } else { - // don't do anything, preference pane doesn't exist. + isDeviceInterfaceConnected = false + DispatchQueue.main.async { + self.setStatusIcon(icon: "WiFiDisconnected") + } + return "USB Wifi is disconnected." } } - @objc func openNetwork() { - // open the Network pane, no need to check because every Mac *should* have this. - NSWorkspace.shared.open(URL(fileURLWithPath: "/System/Library/PreferencePanes/Network.prefPane")) + + func userNotificationCenter(_ center: NSUserNotificationCenter, + shouldPresent notification: NSUserNotification) -> Bool { + return true } - func applicationDidFinishLaunching(_ aNotification: Notification) { - // after the application launches, change the icon accordingly and create the menu. - changeIcon() - constructMenu() + + func setStatusIcon(icon: String) { + if let button = statusItem.button { + if button.image?.name() != icon { + button.image = NSImage(named: NSImage.Name(icon)) + } } + } + + func applicationDidFinishLaunching(_ notification: Notification) { + constructMenu() + } + func applicationWillTerminate(_ aNotification: Notification) { // Insert code here to tear down your application } - func constructMenu() { - // create the menu, with titles, actions, and keyEquivalents. didn't include many keyEquivs because the menu has to be open.. - let menu = NSMenu() - menu.addItem(NSMenuItem(title: "DWA-140 Shortcut (GitHub)", action: #selector(openCredits), keyEquivalent: "")) - menu.addItem(NSMenuItem.separator()) - menu.addItem(NSMenuItem(title: ethStatus(), action: nil, keyEquivalent: "")) - menu.addItem(NSMenuItem(title: "Refresh Status", action: #selector(refreshStatus), keyEquivalent: "r")) - menu.addItem(NSMenuItem(title: "Open DWA-140" , action: #selector(openDWA), keyEquivalent: "t1")) - menu.addItem(NSMenuItem(title: "Network Preferences" , action: #selector(openNetwork), keyEquivalent: "")) - menu.addItem(NSMenuItem.separator()) - menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) - statusItem.menu = menu - } } diff --git a/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/AirPortWarning.png b/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/AirPortWarning.png Binary files differnew file mode 100644 index 0000000..7ed49c8 --- /dev/null +++ b/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/AirPortWarning.png diff --git a/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/AirPortWarning@2x.png b/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/AirPortWarning@2x.png Binary files differnew file mode 100644 index 0000000..499f3a1 --- /dev/null +++ b/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/AirPortWarning@2x.png diff --git a/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/Contents.json b/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/Contents.json new file mode 100644 index 0000000..b552aed --- /dev/null +++ b/DWA140Menu/Assets.xcassets/WiFiCaptive.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "AirPortWarning.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AirPortWarning@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +}
\ No newline at end of file diff --git a/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/AirPortError.png b/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/AirPortError.png Binary files differnew file mode 100644 index 0000000..3ff84b0 --- /dev/null +++ b/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/AirPortError.png diff --git a/DWA140Menu/Assets.xcassets/WifiError.imageset/AirPortError-1_55A54008AD1BA589AA210D2629C1DF41_0.png b/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/AirPortError@2x.png Binary files differindex 039d66f..039d66f 100644 --- a/DWA140Menu/Assets.xcassets/WifiError.imageset/AirPortError-1_55A54008AD1BA589AA210D2629C1DF41_0.png +++ b/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/AirPortError@2x.png diff --git a/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/Contents.json b/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/Contents.json new file mode 100644 index 0000000..f8e3ff5 --- /dev/null +++ b/DWA140Menu/Assets.xcassets/WiFiDisconnected.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "AirPortError.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AirPortError@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +}
\ No newline at end of file diff --git a/DWA140Menu/Assets.xcassets/WifiConnected.imageset/WifiConnected.png b/DWA140Menu/Assets.xcassets/WifiConnected.imageset/AirPort4.png Binary files differindex 11ec726..11ec726 100644 --- a/DWA140Menu/Assets.xcassets/WifiConnected.imageset/WifiConnected.png +++ b/DWA140Menu/Assets.xcassets/WifiConnected.imageset/AirPort4.png diff --git a/DWA140Menu/Assets.xcassets/WifiConnected.imageset/AirPort4@2x.png b/DWA140Menu/Assets.xcassets/WifiConnected.imageset/AirPort4@2x.png Binary files differnew file mode 100644 index 0000000..0d5875f --- /dev/null +++ b/DWA140Menu/Assets.xcassets/WifiConnected.imageset/AirPort4@2x.png diff --git a/DWA140Menu/Assets.xcassets/WifiConnected.imageset/Contents.json b/DWA140Menu/Assets.xcassets/WifiConnected.imageset/Contents.json index 60f1a5a..b547efe 100644 --- a/DWA140Menu/Assets.xcassets/WifiConnected.imageset/Contents.json +++ b/DWA140Menu/Assets.xcassets/WifiConnected.imageset/Contents.json @@ -2,11 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "WifiConnected.png", + "filename" : "AirPort4.png", "scale" : "1x" }, { "idiom" : "universal", + "filename" : "AirPort4@2x.png", "scale" : "2x" }, { diff --git a/DWA140Menu/Info.plist b/DWA140Menu/Info.plist index a1e0606..d682a29 100644 --- a/DWA140Menu/Info.plist +++ b/DWA140Menu/Info.plist @@ -17,7 +17,7 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>1.0.1</string> + <string>1.0.2</string> <key>CFBundleVersion</key> <string>1</string> <key>LSApplicationCategoryType</key> |
