Skip to content

Commit

Permalink
Merge pull request wakatime#88 from wakatime/feature/13-older-macos-v…
Browse files Browse the repository at this point in the history
…ersion-support

Support older macos versions
  • Loading branch information
alanhamlett authored Jun 13, 2023
2 parents 2a6ca92 + bd554a8 commit 745b9fc
Show file tree
Hide file tree
Showing 16 changed files with 463 additions and 145 deletions.
26 changes: 24 additions & 2 deletions .github/workflows/on_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,17 @@ jobs:
# The password used to import the PKCS12 file.
p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
-
name: Codesign
name: Codesign Helper
run: |
/usr/bin/codesign --force -s "WakaTime" --options runtime "build/Release/WakaTime.app/Contents/Library/LoginItems/WakaTime Helper.app" -v
/usr/bin/codesign --verify --deep --strict --verbose=2 "build/Release/WakaTime.app/Contents/Library/LoginItems/WakaTime Helper.app"
-
name: Codesign libswift_Concurrency.dylib
run: |
/usr/bin/codesign --force -s "WakaTime" --options runtime build/Release/WakaTime.app/Contents/Frameworks/libswift_Concurrency.dylib -v
/usr/bin/codesign --verify --deep --strict --verbose=2 build/Release/WakaTime.app/Contents/Frameworks/libswift_Concurrency.dylib
-
name: Codesign App
run: |
/usr/bin/codesign --force -s "WakaTime" --options runtime build/Release/WakaTime.app -v
/usr/bin/codesign --verify --deep --strict --verbose=2 build/Release/WakaTime.app
Expand All @@ -117,7 +127,19 @@ jobs:
NOTARIZATION_PWD: ${{ secrets.AC_PASSWORD }}
run: xcrun notarytool store-credentials "notarytool-profile" --apple-id "$NOTARIZATION_APPLE_ID" --team-id "$NOTARIZATION_TEAM_ID" --password "$NOTARIZATION_PWD"
-
name: Notarize
name: Notarize Helper
run: |
ditto -c -k --keepParent "build/Release/WakaTime.app/Contents/Library/LoginItems/WakaTime Helper.app" helper.zip
xcrun notarytool submit helper.zip --keychain-profile "notarytool-profile" --wait
xcrun stapler staple "build/Release/WakaTime.app/Contents/Library/LoginItems/WakaTime Helper.app"
-
name: Notarize libswift_Concurrency.dylib
run: |
ditto -c -k --keepParent build/Release/WakaTime.app/Contents/Frameworks/libswift_Concurrency.dylib concurrency.zip
xcrun notarytool submit concurrency.zip --keychain-profile "notarytool-profile" --wait
xcrun stapler staple build/Release/WakaTime.app/Contents/Frameworks/libswift_Concurrency.dylib
-
name: Notarize App
run: |
ditto -c -k --keepParent build/Release/WakaTime.app main.zip
xcrun notarytool submit main.zip --keychain-profile "notarytool-profile" --wait
Expand Down
31 changes: 31 additions & 0 deletions WakaTime Helper/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Cocoa

class AppDelegate: NSObject, NSApplicationDelegate {
struct Constants {
static let mainAppBundleID = "macos-wakatime.WakaTime"
}

func applicationDidFinishLaunching(_ aNotification: Notification) {
let runningApps = NSWorkspace.shared.runningApplications
let isRunning = runningApps.contains {
$0.bundleIdentifier == Constants.mainAppBundleID
}

if !isRunning {
var path = Bundle.main.bundlePath as NSString
for _ in 1...4 {
path = path.deletingLastPathComponent as NSString
}
let fileURL = URL(fileURLWithPath: path as String)
print("Opening", fileURL.absoluteString)
NSWorkspace.shared.openApplication(
at: fileURL,
configuration: NSWorkspace.OpenConfiguration()
) { _, error in
if let error {
print(error.localizedDescription)
}
}
}
}
}
8 changes: 8 additions & 0 deletions WakaTime Helper/WakaTime Helper-Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSBackgroundOnly</key>
<true/>
</dict>
</plist>
6 changes: 6 additions & 0 deletions WakaTime Helper/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Cocoa

let delegate = AppDelegate()
let application = NSApplication.shared
application.delegate = delegate
application.run()
75 changes: 75 additions & 0 deletions WakaTime/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Cocoa

class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
var statusBarItem: NSStatusItem!
var settingsWindowController = SettingsWindowController()
var monitoredAppsWindowController = MonitoredAppsWindowController()
var wakaTime: WakaTime?

func applicationDidFinishLaunching(_ aNotification: Notification) {
wakaTime = WakaTime()

// Handle deep links
let eventManager = NSAppleEventManager.shared()
eventManager.setEventHandler(
self,
andSelector: #selector(handleGetURL(_:withReplyEvent:)),
forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL)
)

let statusBar = NSStatusBar.system
statusBarItem = statusBar.statusItem(withLength: NSStatusItem.variableLength)
statusBarItem.button?.image = NSImage(named: NSImage.Name("WakaTime"))

let menu = NSMenu()

menu.addItem(withTitle: "Dashboard", action: #selector(AppDelegate.dashboardClicked(_:)), keyEquivalent: "")
menu.addItem(withTitle: "Settings", action: #selector(AppDelegate.settingsClicked(_:)), keyEquivalent: "")
menu.addItem(withTitle: "Monitored Apps", action: #selector(AppDelegate.monitoredAppsClicked(_:)), keyEquivalent: "")
menu.addItem(NSMenuItem.separator())
menu.addItem(withTitle: "Quit", action: #selector(AppDelegate.quitClicked(_:)), keyEquivalent: "")

statusBarItem.menu = menu
}

@objc func handleGetURL(_ event: NSAppleEventDescriptor, withReplyEvent replyEvent: NSAppleEventDescriptor) {
// Handle deep links
guard let urlString = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue,
let url = URL(string: urlString),
url.scheme == "wakatime"
else { return }

if url.host == "settings" {
showSettings()
}
}

@objc func dashboardClicked(_ sender: AnyObject) {
if let url = URL(string: "https://wakatime.com/") {
NSWorkspace.shared.open(url)
}
}

@objc func settingsClicked(_ sender: AnyObject) {
showSettings()
}

@objc func monitoredAppsClicked(_ sender: AnyObject) {
showMonitoredApps()
}

@objc func quitClicked(_ sender: AnyObject) {
NSApplication.shared.terminate(self)
}

private func showSettings() {
NSApp.activate(ignoringOtherApps: true)
settingsWindowController.showWindow(self)
}

private func showMonitoredApps() {
NSApp.activate(ignoringOtherApps: true)
monitoredAppsWindowController.showWindow(self)
}
}
35 changes: 35 additions & 0 deletions WakaTime/Controls/WKTextField.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// WKTextField.swift
// WakaTime
//
// Created by Tobias Lensing on 07.06.23.
//

import AppKit

class WKTextField: NSTextField {
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if event.type == NSEvent.EventType.keyDown {
let modifierFlags = event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue
if modifierFlags == NSEvent.ModifierFlags.command.rawValue {
switch event.charactersIgnoringModifiers?.first {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true }
case "a":
if NSApp.sendAction(#selector(NSText.selectAll(_:)), to: nil, from: self) { return true }
case "z":
if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true }
default:
break
}
} else if modifierFlags == NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue {
if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true }
}
}
return super.performKeyEquivalent(with: event)
}
}
2 changes: 1 addition & 1 deletion WakaTime/Helpers/AppInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ class AppInfo {
static func getIcon(bundleId: String) -> NSImage? {
guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleId) else { return nil }

return getIcon(file: url.path())
return getIcon(file: url.absoluteURL.path)
}
}
53 changes: 42 additions & 11 deletions WakaTime/Helpers/SettingsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,57 @@ import Foundation
import ServiceManagement

class SettingsManager {
#if !SIMULATE_OLD_MACOS
static let simulateOldMacOS = false
#else
static let simulateOldMacOS = true
#endif

static func loginItemRegistered() -> Bool {
if #available(macOS 13.0, *) {
return SMAppService.mainApp.status != .notFound
} else {
return false
}
}

static func registerAsLoginItem() {
PropertiesManager.shouldLaunchOnLogin = true

do {
try SMAppService.mainApp.register()
print("Registered for login")
} catch let error {
print(error)
// Use SMAppService on macOS 13 or newer to add WakaTime to the "Open at Login" list and SMLoginItemSetEnabled
// for older versions of macOS to add WakaTime to the "Allow in Background" list
if #available(macOS 13.0, *), !simulateOldMacOS {
do {
try SMAppService.mainApp.register()
print("Registered for login")
} catch let error {
print(error)
}
} else {
if SMLoginItemSetEnabled("macos-wakatime.WakaTimeHelper" as CFString, true) {
print("Login item enabled successfully.")
} else {
print("Failed to enable login item.")
}
}
}

static func unregisterAsLoginItem() {
PropertiesManager.shouldLaunchOnLogin = false

do {
try SMAppService.mainApp.unregister()
print("Unregistered for login")
} catch let error {
print(error)
if #available(macOS 13.0, *), !simulateOldMacOS {
do {
try SMAppService.mainApp.unregister()
print("Unregistered for login")
} catch let error {
print(error)
}
} else {
if SMLoginItemSetEnabled("macos-wakatime.WakaTimeHelper" as CFString, false) {
print("Login item disabled successfully.")
} else {
print("Failed to disable login item.")
}
}
}

}
58 changes: 0 additions & 58 deletions WakaTime/Settings.swift

This file was deleted.

23 changes: 7 additions & 16 deletions WakaTime/Views/MonitoredAppsView.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Foundation
import AppKit
import SwiftUI

class MonitoredAppsView: NSView {
func viewDidLoad() {
init() {
super.init(frame: .zero)

let stackView = NSStackView(frame: .zero)
stackView.orientation = .vertical
stackView.distribution = .equalSpacing
Expand All @@ -20,6 +20,10 @@ class MonitoredAppsView: NSView {
buildView(stackView: stackView)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func buildView(stackView: NSStackView) {
for (index, bundleId) in MonitoringManager.appIDsToWatch.enumerated() {
guard
Expand Down Expand Up @@ -76,16 +80,3 @@ class MonitoredAppsView: NSView {
MonitoringManager.set(monitoringState: sender.state == .on ? .on : .off, for: bundleId)
}
}

struct MonitoredAppsViewRepresentable: NSViewRepresentable {
func makeNSView(context: Context) -> NSView {
let view = MonitoredAppsView()
// Configure your NSView here
view.viewDidLoad()
return view
}

func updateNSView(_ nsView: NSView, context: Context) {
// Update your NSView here
}
}
Loading

0 comments on commit 745b9fc

Please sign in to comment.