pEp macOS Desktop adapter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

239 lines
9.3 KiB

//
// AppDelegate.swift
// pEpNotifications
//
// Created by Volker Birk on 28.05.20.
// Copyleft © 2020 pp foundation.
// This file is under GNU General Public License 3.0
//
import Cocoa
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()
func updateNow()
func scheduleUpdates()
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 {
@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
var connection: NSXPCConnection!
var service: pEpMacOSAdapterProtocol!
var nc = NSUserNotificationCenter.default
var clientListener: NSXPCListener!
var receiver: pEpNotification!
@objc func installMenuExtra() {
if statusBarItem == nil {
statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
statusBarItem?.button?.title = "p≡p"
statusBarItem?.menu = NSApp.menu?.item(at: 0)?.submenu
_updateNow.action = #selector(updateNow)
}
}
func uninstallMenuExtra() {
if (statusBarItem != nil && !UserDefaults.standard.bool(forKey: "AlwaysShowThisMenu")) {
NSStatusBar.system.removeStatusItem(statusBarItem!)
statusBarItem = nil
}
}
func setDownloadState(_ text: String, _ product: Dictionary<String, Any>? = nil) {
if product == nil {
statusText.title = NSLocalizedString(text, comment: "")
statusText.isEnabled = false
}
else {
let name = product?["name"] as! String
statusText.title = NSString.localizedStringWithFormat(NSLocalizedString(text, comment: "") as NSString, name) as String
statusText.isEnabled = true
}
statusText.representedObject = product
}
@IBAction func scheduleUpdates(_ sender: NSMenuItem) {
if sender.state == NSControl.StateValue.on {
sender.state = NSControl.StateValue.off
UserDefaults.standard.set(false, forKey: "ScheduleUpdates")
service.stopUpdates()
}
else {
sender.state = NSControl.StateValue.on
UserDefaults.standard.set(true, forKey: "ScheduleUpdates")
service.scheduleUpdates()
}
}
@IBAction func alwaysShowThisMenu(_ sender: NSMenuItem) {
if sender.state == NSControl.StateValue.on {
sender.state = NSControl.StateValue.off
UserDefaults.standard.set(false, forKey: "AlwaysShowThisMenu")
if statusText.representedObject == nil {
uninstallMenuExtra()
}
}
else {
sender.state = NSControl.StateValue.on
UserDefaults.standard.set(true, forKey: "AlwaysShowThisMenu")
}
}
@IBAction func installNow(_ sender: NSMenuItem) {
uninstallMenuExtra()
let product = sender.representedObject as! Dictionary<String, Any>
let un = product["notification"] as! NSUserNotification
nc.removeDeliveredNotification(un)
NSLog("pEpNotifications: installNow clicked for %@", product["name"] as! String)
NSWorkspace.shared.openFile(product["filename"] as! String)
sender.representedObject = nil
setDownloadState("Connected.")
}
func notifyDownload(_ type: Int, withName: NSString, withFilename: NSString)
{
let _type = DNType.init(rawValue: type)
switch _type {
case .downloading:
NSLog("pEpNotifications: downloading")
setDownloadState(String(format: NSLocalizedString("Downloading update for %@…", comment: ""), withName))
self.performSelector(onMainThread: #selector(installMenuExtra), with:nil, waitUntilDone: false)
case .downloadArrived:
let un = NSUserNotification()
un.title = NSLocalizedString("Update available", comment: "")
un.informativeText = String(format: NSLocalizedString("A new update for %@ is ready to be installed.", comment: ""), withName)
un.actionButtonTitle = NSLocalizedString("Install", comment: "")
un.hasActionButton = true
un.otherButtonTitle = NSLocalizedString("Not now", comment: "")
un.soundName = NSUserNotificationDefaultSoundName
let installTitle : String = NSLocalizedString("Install", comment: "")
un.additionalActions = [NSUserNotificationAction(identifier: "install", title: installTitle)]
un.setValue(true, forKey: "_showsButtons")
un.userInfo = ["name": withName, "filename": withFilename]
nc.deliver(un)
setDownloadState("New version of %@ available", ["name": withName, "filename": withFilename, "notification": un])
case .noDownloadAvailable:
NSLog("pEpNotifications: no download available")
setDownloadState("The software is up to date.")
case .ready:
NSLog("pEpNotifications: ready.")
setDownloadState("Connected.")
case .none:
break;
}
}
@objc func updateNow() {
nc.removeAllDeliveredNotifications()
service.updateNow()
}
@objc func userNotificationCenter(_ : NSUserNotificationCenter, didActivate: NSUserNotification) {
if didActivate.activationType == NSUserNotification.ActivationType.actionButtonClicked {
uninstallMenuExtra()
let filename : String = didActivate.userInfo?["filename"] as! String;
NSLog("pEpNotifications: actionButtonClicked for %@", filename)
NSWorkspace.shared.openFile(filename)
setDownloadState("Connected.")
}
}
func proxyErrorHandler(err: Error) -> Void {
NSLog("%@", err.localizedDescription)
setDownloadState("Connection failed")
}
@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()
return true
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
// preference defaults
let appDefaults = ["ScheduleUpdates": true, "AlwaysShowThisMenu": true]
UserDefaults.standard.register(defaults: appDefaults)
if UserDefaults.standard.bool(forKey: "AlwaysShowThisMenu") {
_alwaysShowThisMenu.state = NSControl.StateValue.on
installMenuExtra()
}
else {
_alwaysShowThisMenu.state = NSControl.StateValue.off
}
setDownloadState("Connecting…")
// connect to XPC service
// connection = NSXPCConnection.init(machServiceName: "pEp.foundation.pEpMacOSAdapter") // Commented for hacky TB installer using this branch for building upater app and master for xcpservice. Needs "foundation.pEp.adapter.macOS"
connection = NSXPCConnection.init(machServiceName: "foundation.pEp.adapter.macOS")
if connection != nil {
connection.remoteObjectInterface = NSXPCInterface.init(with: pEpMacOSAdapterProtocol.self)
connection.resume()
service = connection.remoteObjectProxyWithErrorHandler(proxyErrorHandler) as? pEpMacOSAdapterProtocol
// init client callback service
clientListener = NSXPCListener.anonymous()
clientListener.delegate = self
clientListener.resume()
// subscribe and schedule updates
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
}
service.updateNow()
}
else {
NSLog("pEpNotifications: %@", "cannot connect to pEp.foundation.adapter.macOS")
}
nc.removeAllDeliveredNotifications()
nc.delegate = self
}
func applicationWillTerminate(_ aNotification: Notification) {
service.unsubscribeForUpdate()
connection?.invalidate()
}
}