|
|
|
@ -12,10 +12,6 @@ import SwiftUI
|
|
|
|
|
|
|
|
|
|
enum DNType : Int { case ready = 0, downloading, downloadArrived, noDownloadAvailable }
|
|
|
|
|
|
|
|
|
|
@objc(PEPNotificationProtocol) protocol PEPNotificationProtocol {
|
|
|
|
|
func notifyDownload(_ type: Int, withName: NSString, withFilename: NSString)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc(PEPMacOSAdapterProtocol) protocol PEPMacOSAdapterProtocol {
|
|
|
|
|
func subscribeForUpdate(_ endpoint: NSXPCListenerEndpoint?)
|
|
|
|
|
func unsubscribeForUpdate()
|
|
|
|
@ -24,52 +20,28 @@ enum DNType : Int { case ready = 0, downloading, downloadArrived, noDownloadAvai
|
|
|
|
|
func stopUpdates()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc class pEpNotification : NSObject, PEPNotificationProtocol {
|
|
|
|
|
var delegate: PEPNotificationProtocol!
|
|
|
|
|
|
|
|
|
|
func notifyDownload(_ type: Int, withName: NSString, withFilename: NSString) {
|
|
|
|
|
NSLog("notifyDownload")
|
|
|
|
|
delegate?.notifyDownload(type, withName: withName, withFilename: withFilename)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@NSApplicationMain
|
|
|
|
|
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate, NSXPCListenerDelegate, PEPNotificationProtocol {
|
|
|
|
|
class AppDelegate: NSObject {
|
|
|
|
|
|
|
|
|
|
// MARK: - Outlets
|
|
|
|
|
|
|
|
|
|
@IBOutlet weak var pEpMenu: NSMenu!
|
|
|
|
|
@IBOutlet weak var statusText: NSMenuItem!
|
|
|
|
|
@IBOutlet weak var _updateNow: NSMenuItem!
|
|
|
|
|
@IBOutlet weak var _scheduleUpdates: NSMenuItem!
|
|
|
|
|
@IBOutlet weak var _alwaysShowThisMenu: NSMenuItem!
|
|
|
|
|
|
|
|
|
|
var statusBarItem: NSStatusItem? = nil
|
|
|
|
|
lazy var connection: NSXPCConnection = NSXPCConnection.init(machServiceName: "foundation.pEp.adapter.macOS")
|
|
|
|
|
var service: PEPMacOSAdapterProtocol?
|
|
|
|
|
var nc = NSUserNotificationCenter.default
|
|
|
|
|
lazy var clientListener: NSXPCListener = NSXPCListener.anonymous()
|
|
|
|
|
var receiver: pEpNotification!
|
|
|
|
|
lazy var downloadStateNotifier = DownloadStateNotifier(at: statusText)
|
|
|
|
|
// MARK: - Properties
|
|
|
|
|
|
|
|
|
|
@objc func installMenuExtra() {
|
|
|
|
|
guard statusBarItem == nil else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
|
|
|
|
statusBarItem?.button?.title = "p≡p"
|
|
|
|
|
statusBarItem?.menu = NSApp.menu?.item(at: 0)?.submenu
|
|
|
|
|
_updateNow.action = #selector(updateNow)
|
|
|
|
|
}
|
|
|
|
|
private var statusBarItem: NSStatusItem? = nil
|
|
|
|
|
private lazy var connection: NSXPCConnection = NSXPCConnection.init(machServiceName: "foundation.pEp.adapter.macOS")
|
|
|
|
|
private var service: PEPMacOSAdapterProtocol?
|
|
|
|
|
private var nc = NSUserNotificationCenter.default
|
|
|
|
|
private lazy var clientListener: NSXPCListener = NSXPCListener.anonymous()
|
|
|
|
|
private var receiver: PEPNotification?
|
|
|
|
|
private lazy var downloadStateNotifier = DownloadStateNotifier(at: statusText)
|
|
|
|
|
|
|
|
|
|
func uninstallMenuExtra() {
|
|
|
|
|
guard
|
|
|
|
|
statusBarItem != nil && !UserDefaults.standard.bool(forKey: "AlwaysShowThisMenu")
|
|
|
|
|
else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSStatusBar.system.removeStatusItem(statusBarItem!)
|
|
|
|
|
statusBarItem = nil
|
|
|
|
|
}
|
|
|
|
|
// MARK: - Action methods
|
|
|
|
|
|
|
|
|
|
@IBAction func scheduleUpdates(_ sender: NSMenuItem) {
|
|
|
|
|
if sender.state == NSControl.StateValue.on {
|
|
|
|
@ -106,6 +78,104 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|
|
|
|
sender.representedObject = nil
|
|
|
|
|
downloadStateNotifier.notify(.Connected)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Private
|
|
|
|
|
|
|
|
|
|
extension AppDelegate {
|
|
|
|
|
@objc
|
|
|
|
|
private func updateNow() {
|
|
|
|
|
nc.removeAllDeliveredNotifications()
|
|
|
|
|
service?.updateNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func setupAppDefaults() {
|
|
|
|
|
let appDefaults = ["ScheduleUpdates": true, "AlwaysShowThisMenu": false]
|
|
|
|
|
UserDefaults.standard.register(defaults: appDefaults)
|
|
|
|
|
if UserDefaults.standard.bool(forKey: "AlwaysShowThisMenu") {
|
|
|
|
|
_alwaysShowThisMenu.state = NSControl.StateValue.on
|
|
|
|
|
installMenuExtra()
|
|
|
|
|
} else {
|
|
|
|
|
_alwaysShowThisMenu.state = NSControl.StateValue.off
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func initXPCConnection() {
|
|
|
|
|
connection.remoteObjectInterface = NSXPCInterface.init(with: PEPMacOSAdapterProtocol.self)
|
|
|
|
|
connection.resume()
|
|
|
|
|
service = connection.remoteObjectProxyWithErrorHandler(proxyErrorHandler) as? PEPMacOSAdapterProtocol
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func initClientService() {
|
|
|
|
|
clientListener.delegate = self
|
|
|
|
|
clientListener.resume()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func setupServiceSubscription() {
|
|
|
|
|
service?.subscribeForUpdate(clientListener.endpoint)
|
|
|
|
|
if UserDefaults.standard.bool(forKey: "ScheduleUpdates") {
|
|
|
|
|
service?.scheduleUpdates()
|
|
|
|
|
_scheduleUpdates.state = NSControl.StateValue.on
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
service?.stopUpdates()
|
|
|
|
|
_scheduleUpdates.state = NSControl.StateValue.off
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func initNotificationCenter() {
|
|
|
|
|
nc.removeAllDeliveredNotifications()
|
|
|
|
|
nc.delegate = self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func proxyErrorHandler(err: Error) -> Void {
|
|
|
|
|
NSLog("%@", err.localizedDescription)
|
|
|
|
|
downloadStateNotifier.notify(.ConnectionFailed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// This removes _all_ previous notifications from a pEp product.
|
|
|
|
|
/// Currently there is only one pEp Product (pEp4Thunderbird) to take care of.
|
|
|
|
|
/// - Parameter productName: the pEp product name
|
|
|
|
|
private func removeDeliveredNotifications(with productName: NSString) {
|
|
|
|
|
nc.deliveredNotifications.forEach { notification in
|
|
|
|
|
guard
|
|
|
|
|
let userInfoName = notification.userInfo?["name"] as? NSString,
|
|
|
|
|
userInfoName == productName
|
|
|
|
|
else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nc.removeDeliveredNotification(notification)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func installMenuExtra() {
|
|
|
|
|
guard statusBarItem == nil else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
|
|
|
|
statusBarItem?.button?.title = "p≡p"
|
|
|
|
|
statusBarItem?.menu = NSApp.menu?.item(at: 0)?.submenu
|
|
|
|
|
_updateNow.action = #selector(updateNow)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func uninstallMenuExtra() {
|
|
|
|
|
guard
|
|
|
|
|
statusBarItem != nil && !UserDefaults.standard.bool(forKey: "AlwaysShowThisMenu")
|
|
|
|
|
else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSStatusBar.system.removeStatusItem(statusBarItem!)
|
|
|
|
|
statusBarItem = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - PEPNotificationProtocol
|
|
|
|
|
|
|
|
|
|
extension AppDelegate: PEPNotificationProtocol {
|
|
|
|
|
|
|
|
|
|
func notifyDownload(_ type: Int, withName: NSString, withFilename: NSString) {
|
|
|
|
|
let _type = DNType.init(rawValue: type)
|
|
|
|
@ -113,7 +183,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|
|
|
|
case .downloading:
|
|
|
|
|
NSLog("pEpNotifications: downloading")
|
|
|
|
|
downloadStateNotifier.notify(.Downloading(String(withName)))
|
|
|
|
|
self.performSelector(onMainThread: #selector(installMenuExtra), with:nil, waitUntilDone: false)
|
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
|
|
guard let me = self else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
me.installMenuExtra()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case .downloadArrived:
|
|
|
|
|
removeDeliveredNotifications(with: withName)
|
|
|
|
@ -145,57 +220,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// This removes _all_ previous notifications from a pEp product.
|
|
|
|
|
/// Currently there is only one pEp Product (pEp4Thunderbird) to take care of.
|
|
|
|
|
/// - Parameter productName: the pEp product name
|
|
|
|
|
private func removeDeliveredNotifications(with productName: NSString) {
|
|
|
|
|
nc.deliveredNotifications.forEach { notification in
|
|
|
|
|
guard
|
|
|
|
|
let userInfoName = notification.userInfo?["name"] as? NSString,
|
|
|
|
|
userInfoName == productName
|
|
|
|
|
else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nc.removeDeliveredNotification(notification)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func updateNow() {
|
|
|
|
|
nc.removeAllDeliveredNotifications()
|
|
|
|
|
service?.updateNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func userNotificationCenter(_ : NSUserNotificationCenter, didActivate: NSUserNotification) {
|
|
|
|
|
guard
|
|
|
|
|
didActivate.activationType == NSUserNotification.ActivationType.actionButtonClicked
|
|
|
|
|
else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uninstallMenuExtra()
|
|
|
|
|
let filename : String = didActivate.userInfo?["filename"] as! String
|
|
|
|
|
NSLog("pEpNotifications: actionButtonClicked for %@", filename)
|
|
|
|
|
NSWorkspace.shared.openFile(filename)
|
|
|
|
|
downloadStateNotifier.notify(.Connected)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func proxyErrorHandler(err: Error) -> Void {
|
|
|
|
|
NSLog("%@", err.localizedDescription)
|
|
|
|
|
downloadStateNotifier.notify(.ConnectionFailed)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
|
|
|
|
|
newConnection.exportedInterface = NSXPCInterface.init(with: PEPNotificationProtocol.self)
|
|
|
|
|
let obj = pEpNotification()
|
|
|
|
|
obj.delegate = self
|
|
|
|
|
newConnection.exportedObject = obj
|
|
|
|
|
newConnection.resume()
|
|
|
|
|
// MARK: - NSApplicationDelegate
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension AppDelegate: NSApplicationDelegate {
|
|
|
|
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
|
|
|
|
// preference defaults
|
|
|
|
|
setupAppDefaults()
|
|
|
|
@ -220,43 +249,36 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|
|
|
|
service?.unsubscribeForUpdate()
|
|
|
|
|
connection.invalidate()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func setupAppDefaults() {
|
|
|
|
|
let appDefaults = ["ScheduleUpdates": true, "AlwaysShowThisMenu": false]
|
|
|
|
|
UserDefaults.standard.register(defaults: appDefaults)
|
|
|
|
|
if UserDefaults.standard.bool(forKey: "AlwaysShowThisMenu") {
|
|
|
|
|
_alwaysShowThisMenu.state = NSControl.StateValue.on
|
|
|
|
|
installMenuExtra()
|
|
|
|
|
} else {
|
|
|
|
|
_alwaysShowThisMenu.state = NSControl.StateValue.off
|
|
|
|
|
// MARK: - NSUserNotificationCenterDelegate
|
|
|
|
|
|
|
|
|
|
extension AppDelegate: NSUserNotificationCenterDelegate {
|
|
|
|
|
@objc func userNotificationCenter(_ : NSUserNotificationCenter, didActivate: NSUserNotification) {
|
|
|
|
|
guard
|
|
|
|
|
didActivate.activationType == NSUserNotification.ActivationType.actionButtonClicked
|
|
|
|
|
else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func initXPCConnection() {
|
|
|
|
|
connection.remoteObjectInterface = NSXPCInterface.init(with: PEPMacOSAdapterProtocol.self)
|
|
|
|
|
connection.resume()
|
|
|
|
|
service = connection.remoteObjectProxyWithErrorHandler(proxyErrorHandler) as? PEPMacOSAdapterProtocol
|
|
|
|
|
uninstallMenuExtra()
|
|
|
|
|
let filename : String = didActivate.userInfo?["filename"] as! String
|
|
|
|
|
NSLog("pEpNotifications: actionButtonClicked for %@", filename)
|
|
|
|
|
NSWorkspace.shared.openFile(filename)
|
|
|
|
|
downloadStateNotifier.notify(.Connected)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func initClientService() {
|
|
|
|
|
clientListener.delegate = self
|
|
|
|
|
clientListener.resume()
|
|
|
|
|
}
|
|
|
|
|
// MARK: - NSXPCListenerDelegate
|
|
|
|
|
|
|
|
|
|
private func setupServiceSubscription() {
|
|
|
|
|
service?.subscribeForUpdate(clientListener.endpoint)
|
|
|
|
|
if UserDefaults.standard.bool(forKey: "ScheduleUpdates") {
|
|
|
|
|
service?.scheduleUpdates()
|
|
|
|
|
_scheduleUpdates.state = NSControl.StateValue.on
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
service?.stopUpdates()
|
|
|
|
|
_scheduleUpdates.state = NSControl.StateValue.off
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
extension AppDelegate: NSXPCListenerDelegate {
|
|
|
|
|
@objc func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
|
|
|
|
|
newConnection.exportedInterface = NSXPCInterface.init(with: PEPNotificationProtocol.self)
|
|
|
|
|
let obj = PEPNotification()
|
|
|
|
|
obj.delegate = self
|
|
|
|
|
newConnection.exportedObject = obj
|
|
|
|
|
newConnection.resume()
|
|
|
|
|
|
|
|
|
|
private func initNotificationCenter() {
|
|
|
|
|
nc.removeAllDeliveredNotifications()
|
|
|
|
|
nc.delegate = self
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|