//
|
|
// AppDelegate.swift
|
|
// pEpNotifications
|
|
//
|
|
// Created by Volker Birk on 28.05.20.
|
|
// Copyright © 2020 p≡p foundation. All rights reserved.
|
|
// 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": false]
|
|
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: "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()
|
|
}
|
|
}
|