|
|
- //
- // AppDelegate.swift
- // pEpNotifications
- //
- // Created by Volker Birk on 28.05.20.
- // Copyleft © 2020 p≡p 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()
- }
- }
|