Merge branch 'Release_2.1' into APPLEMAIL-31

* Release_2.1:
  MOS-11 Change menu title to 'p≡p updates'. Change menu item title to 'Check for Updates'. Change menu item title to 'Keep software up to date'.
  MOS-9 Change statusBarITem to variableLength so we can see the menu text properly.
  MOS-8 Rename menuitems properties.
  MOS-8 Split to extensions. Create PEPNotification. Refactor.
  MOS-8 Change pEpMacOSAdapterProtocol to PEPMacOSAdapterProtocol.
  MOS-8 Change pEpNotificationProtocol to PEPNotificationProtocol.
  MOS-8 Create DownloadStateNotifier, which is responsible to show state messages in statusText menu item.
  MOS-8 Split applicationDidFinishLaunching to private functions to improve readability.
  MOS-8 Delete @objc and add private to removeDeliveredNotifications.
  MOS-8 localizedStringWithFormat to String.
  MOS-8 Add guard when needed. Change else statements to right place.
  MOS-8 Add lazy to connection and related changes.
  MOS-8 Remove ! from clientListener and related changes.
  MOS-8 Remove ! from service and related changes.
  MOS-8 Remove ! from connection and related changes.
  MOS-8 Delete self when not needed. Move comments for removeDeliveredNotifications.
  MOS-8 Delete all previous product notifications before sending a new one.
APPLEMAIL-31
David Alarcon 2 years ago
commit 105d539107
  1. 4
      README.md
  2. 12
      Submodules/pEpNotifications/pEpNotifications.xcodeproj/project.pbxproj
  3. 322
      Submodules/pEpNotifications/pEpNotifications/AppDelegate.swift
  4. 18
      Submodules/pEpNotifications/pEpNotifications/Base.lproj/Main.storyboard
  5. 53
      Submodules/pEpNotifications/pEpNotifications/DownloadStateNotifier.swift
  6. 27
      Submodules/pEpNotifications/pEpNotifications/PEPNotification.swift
  7. 12
      Submodules/pEpNotifications/pEpNotifications/en.lproj/Main.strings
  8. 4
      pEpMacOSAdapter.xcodeproj/project.pbxproj
  9. 8
      pEpMacOSAdapter/PEPMacOSAdapterProtocol.h
  10. 2
      pEpMacOSAdapter/main.m
  11. 4
      pEpMacOSAdapter/pEpMacOSAdapter.h
  12. 8
      pEpMacOSAdapter/pEpMacOSAdapter.m
  13. 4
      pEpMacOSAdapter/pEpUpdater.h

@ -117,9 +117,9 @@ The implementation is split in to parts:
1. the working code is in pEpMacOSAdapter; the implementation is in Objective C
1. the User Interface is in pEpNotifications; the implementation is in Swift
### pEpMacOSAdapterProtocol, implemented with XPC
### PEPMacOSAdapterProtocol, implemented with XPC
see [pEpMacOSAdapterProtocol.h](https://gitea.pep.foundation/fdik/pEpMacOSAdapter/src/branch/master/pEpMacOSAdapter/pEpMacOSAdapterProtocol.h)
see [PEPMacOSAdapterProtocol.h](https://gitea.pep.foundation/fdik/pEpMacOSAdapter/src/branch/master/pEpMacOSAdapter/PEPMacOSAdapterProtocol.h)
This protocol is provided to the User Interface to get informed when a download was happening.
When a subscription is active updateNow() can be used to search for immediate updates.

@ -12,6 +12,8 @@
3527B2B924802F87007A6276 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3527B2B824802F87007A6276 /* Preview Assets.xcassets */; };
3594303F2483011000DCBD65 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3594303D2483011000DCBD65 /* Main.storyboard */; };
359430482483264F00DCBD65 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3594304A2483264F00DCBD65 /* Localizable.strings */; };
4E1107AF256FE89900EB1711 /* DownloadStateNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1107AD256FCF2900EB1711 /* DownloadStateNotifier.swift */; };
4E1107B5256FFEC000EB1711 /* PEPNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1107B4256FFEC000EB1711 /* PEPNotification.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -24,10 +26,12 @@
3527B2C724802F87007A6276 /* pEpNotificationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = pEpNotificationsTests.swift; sourceTree = "<group>"; };
3527B2C924802F87007A6276 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3594303E2483011000DCBD65 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
359430432483081B00DCBD65 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
35943044248309BE00DCBD65 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Main.strings; sourceTree = "<group>"; };
359430492483264F00DCBD65 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
3594304B2483266200DCBD65 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
4E1107AD256FCF2900EB1711 /* DownloadStateNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadStateNotifier.swift; sourceTree = "<group>"; };
4E1107B4256FFEC000EB1711 /* PEPNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PEPNotification.swift; sourceTree = "<group>"; };
4E4A391A2575452700BF0A15 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -64,6 +68,8 @@
3594304A2483264F00DCBD65 /* Localizable.strings */,
3594303D2483011000DCBD65 /* Main.storyboard */,
3527B2B124802F84007A6276 /* AppDelegate.swift */,
4E1107AD256FCF2900EB1711 /* DownloadStateNotifier.swift */,
4E1107B4256FFEC000EB1711 /* PEPNotification.swift */,
3527B2B524802F87007A6276 /* Assets.xcassets */,
3527B2BD24802F87007A6276 /* Info.plist */,
3527B2BE24802F87007A6276 /* pEpNotifications.entitlements */,
@ -184,7 +190,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4E1107AF256FE89900EB1711 /* DownloadStateNotifier.swift in Sources */,
3527B2B224802F84007A6276 /* AppDelegate.swift in Sources */,
4E1107B5256FFEC000EB1711 /* PEPNotification.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -195,8 +203,8 @@
isa = PBXVariantGroup;
children = (
3594303E2483011000DCBD65 /* Base */,
359430432483081B00DCBD65 /* en */,
35943044248309BE00DCBD65 /* de */,
4E4A391A2575452700BF0A15 /* en */,
);
name = Main.storyboard;
sourceTree = "<group>";

@ -12,11 +12,7 @@ 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 {
@objc(PEPMacOSAdapterProtocol) protocol PEPMacOSAdapterProtocol {
func subscribeForUpdate(_ endpoint: NSXPCListenerEndpoint?)
func unsubscribeForUpdate()
func updateNow()
@ -24,69 +20,38 @@ 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!
@IBOutlet weak var updateNowMenuItem: NSMenuItem!
@IBOutlet weak var scheduleUpdatesMenuItem: NSMenuItem!
@IBOutlet weak var alwaysShowThisMenuMenuItem: NSMenuItem!
var statusBarItem: NSStatusItem? = nil
var connection: NSXPCConnection!
var service: pEpMacOSAdapterProtocol!
var nc = NSUserNotificationCenter.default
var clientListener: NSXPCListener!
var receiver: pEpNotification!
// MARK: - Properties
@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)
}
}
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() {
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
}
// MARK: - Action methods
@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 {
service?.stopUpdates()
} else {
sender.state = NSControl.StateValue.on
UserDefaults.standard.set(true, forKey: "ScheduleUpdates")
service.scheduleUpdates()
service?.scheduleUpdates()
}
}
@ -97,8 +62,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
if statusText.representedObject == nil {
uninstallMenuExtra()
}
}
else {
} else {
sender.state = NSControl.StateValue.on
UserDefaults.standard.set(true, forKey: "AlwaysShowThisMenu")
}
@ -112,19 +76,123 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
NSLog("pEpNotifications: installNow clicked for %@", product["name"] as! String)
NSWorkspace.shared.openFile(product["filename"] as! String)
sender.representedObject = nil
setDownloadState("Connected.")
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") {
alwaysShowThisMenuMenuItem.state = NSControl.StateValue.on
installMenuExtra()
} else {
alwaysShowThisMenuMenuItem.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()
scheduleUpdatesMenuItem.state = NSControl.StateValue.on
}
else {
service?.stopUpdates()
scheduleUpdatesMenuItem.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.variableLength)
statusBarItem?.button?.title = "p≡p"
statusBarItem?.menu = NSApp.menu?.item(at: 0)?.submenu
updateNowMenuItem.action = #selector(updateNow)
}
private func uninstallMenuExtra() {
guard
statusBarItem != nil && !UserDefaults.standard.bool(forKey: "AlwaysShowThisMenu")
else {
return
}
NSStatusBar.system.removeStatusItem(statusBarItem!)
statusBarItem = nil
}
func notifyDownload(_ type: Int, withName: NSString, withFilename: NSString)
{
}
// MARK: - PEPNotificationProtocol
extension AppDelegate: PEPNotificationProtocol {
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)
downloadStateNotifier.notify(.Downloading(String(withName)))
DispatchQueue.main.async { [weak self] in
guard let me = self else {
return
}
me.installMenuExtra()
}
case .downloadArrived:
removeDeliveredNotifications(with: withName)
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)
@ -137,102 +205,80 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
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])
let product = ["name": withName, "filename": withFilename, "notification": un]
downloadStateNotifier.notify(.NewVersionAvailable(String(withName)), with: product)
case .noDownloadAvailable:
NSLog("pEpNotifications: no download available")
setDownloadState("The software is up to date.")
downloadStateNotifier.notify(.UpToDate)
case .ready:
NSLog("pEpNotifications: ready.")
setDownloadState("Connected.")
downloadStateNotifier.notify(.Connected)
case .none:
break;
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
}
// MARK: - NSApplicationDelegate
extension AppDelegate: NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// preference defaults
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
}
setDownloadState("Connecting…")
downloadStateNotifier.notify(.Connecting)
// connect to XPC service
connection = NSXPCConnection.init(machServiceName: "foundation.pEp.adapter.macOS_OpenStep")
if connection != nil {
connection.remoteObjectInterface = NSXPCInterface.init(with: pEpMacOSAdapterProtocol.self)
connection.resume()
service = connection.remoteObjectProxyWithErrorHandler(proxyErrorHandler) as? pEpMacOSAdapterProtocol
initXPCConnection()
// 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()
}
// init client callback service
initClientService()
// subscribe and schedule updates
setupServiceSubscription()
service?.updateNow()
initNotificationCenter()
}
func applicationWillTerminate(_ aNotification: Notification) {
service?.unsubscribeForUpdate()
connection.invalidate()
}
}
// MARK: - NSUserNotificationCenterDelegate
extension AppDelegate: NSUserNotificationCenterDelegate {
@objc func userNotificationCenter(_ : NSUserNotificationCenter, didActivate: NSUserNotification) {
guard
didActivate.activationType == NSUserNotification.ActivationType.actionButtonClicked
else {
NSLog("pEpNotifications: %@", "cannot connect to pEp.foundation.adapter.macOS")
return
}
nc.removeAllDeliveredNotifications()
nc.delegate = self
uninstallMenuExtra()
let filename : String = didActivate.userInfo?["filename"] as! String
NSLog("pEpNotifications: actionButtonClicked for %@", filename)
NSWorkspace.shared.openFile(filename)
downloadStateNotifier.notify(.Connected)
}
}
func applicationWillTerminate(_ aNotification: Notification) {
service.unsubscribeForUpdate()
connection?.invalidate()
// MARK: - NSXPCListenerDelegate
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()
return true
}
}

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17156"/>
</dependencies>
<scenes>
<!--Application-->
@ -11,9 +11,9 @@
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="pEpNotifications" id="1Xt-HY-uBw">
<menuItem title="p≡p updates" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="pEpNotifications" systemMenu="apple" autoenablesItems="NO" id="uQy-DD-JDr">
<menu key="submenu" title="p≡p updates" systemMenu="apple" autoenablesItems="NO" id="uQy-DD-JDr">
<items>
<menuItem title="Starting…" enabled="NO" id="D45-AS-mhG">
<modifierMask key="keyEquivalentModifierMask"/>
@ -21,11 +21,11 @@
<action selector="installNow:" target="Voe-Tx-rLC" id="ESa-58-lKy"/>
</connections>
</menuItem>
<menuItem title="Update now" id="aUJ-VR-2jH">
<menuItem title="Check for Updates" id="aUJ-VR-2jH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="vo5-AO-stB"/>
<menuItem title="Schedule updates" id="y9Y-ru-Qxs" userLabel="Schedule updates">
<menuItem title="Keep software up to date" id="y9Y-ru-Qxs" userLabel="Schedule updates">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="scheduleUpdates:" target="Voe-Tx-rLC" id="H5q-dk-4ys"/>
@ -54,11 +54,11 @@
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="pEp_updater" customModuleProvider="target">
<connections>
<outlet property="_alwaysShowThisMenu" destination="u5e-3R-3lT" id="cw9-oL-BVd"/>
<outlet property="_scheduleUpdates" destination="y9Y-ru-Qxs" id="JEY-Hx-pbv"/>
<outlet property="_updateNow" destination="aUJ-VR-2jH" id="34x-R2-L4V"/>
<outlet property="alwaysShowThisMenuMenuItem" destination="u5e-3R-3lT" id="cw9-oL-BVd"/>
<outlet property="pEpMenu" destination="uQy-DD-JDr" id="SSj-Qs-zAq"/>
<outlet property="scheduleUpdatesMenuItem" destination="y9Y-ru-Qxs" id="JEY-Hx-pbv"/>
<outlet property="statusText" destination="D45-AS-mhG" id="PNv-G8-o8P"/>
<outlet property="updateNowMenuItem" destination="aUJ-VR-2jH" id="34x-R2-L4V"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>

@ -0,0 +1,53 @@
//
// DownloadStateNotifier.swift
// pEpNotifications
//
// Created by David Alarcon on 26/11/2020.
// Copyright © 2020 pp foundation. All rights reserved.
//
import SwiftUI
typealias Product = Dictionary<String, Any>
struct DownloadStateNotifier {
var menuItem: NSMenuItem
init(at menuItem: NSMenuItem) {
self.menuItem = menuItem
}
func notify(_ state: DownloadState, with product: Product? = nil) {
menuItem.title = state.localizedString()
menuItem.isEnabled = (product != nil)
menuItem.representedObject = product
}
}
public enum DownloadState {
case Connecting
case Connected
case Downloading(String)
case NewVersionAvailable(String)
case UpToDate
case ConnectionFailed
func localizedString() -> String {
switch self {
case .Connecting:
return NSLocalizedString("Connecting...", comment: "")
case .Connected:
return NSLocalizedString("Connected.", comment: "")
case .Downloading(let product):
return String.localizedStringWithFormat(NSLocalizedString("Downloading update for %@…", comment: ""), product)
case .NewVersionAvailable(let product):
return String.localizedStringWithFormat(NSLocalizedString("New version of %@ available", comment: ""), product)
case .UpToDate:
return NSLocalizedString("The software is up to date.", comment: "")
case .ConnectionFailed:
return NSLocalizedString("Connection failed", comment: "")
}
}
}

@ -0,0 +1,27 @@
//
// PEPNotification.swift
// pp updates
//
// Created by David Alarcon on 26/11/2020.
// Copyright © 2020 pp foundation. All rights reserved.
//
import Foundation
@objc(PEPNotificationProtocol) protocol PEPNotificationProtocol {
func notifyDownload(_ type: Int, withName: NSString, withFilename: NSString)
}
@objc class PEPNotification : NSObject {
// Intentionally not weak, No one else holds strong pointer to it.
var delegate: PEPNotificationProtocol?
}
// MARK: - PEPNotificationProtocol
extension PEPNotification: PEPNotificationProtocol {
func notifyDownload(_ type: Int, withName: NSString, withFilename: NSString) {
NSLog("notifyDownload")
delegate?.notifyDownload(type, withName: withName, withFilename: withFilename)
}
}

@ -1,5 +1,5 @@
/* Class = "NSMenuItem"; title = "pEpNotifications"; ObjectID = "1Xt-HY-uBw"; */
/* Class = "NSMenuItem"; title = "p≡p updates"; ObjectID = "1Xt-HY-uBw"; */
"1Xt-HY-uBw.title" = "p≡p updates";
/* Class = "NSMenuItem"; title = "Quit p≡p"; ObjectID = "4sb-4s-VLi"; */
@ -11,8 +11,14 @@
/* Class = "NSMenuItem"; title = "Starting…"; ObjectID = "D45-AS-mhG"; */
"D45-AS-mhG.title" = "Starting…";
/* Class = "NSMenu"; title = "pEpNotifications"; ObjectID = "uQy-DD-JDr"; */
/* Class = "NSMenuItem"; title = "Check for Updates"; ObjectID = "aUJ-VR-2jH"; */
"aUJ-VR-2jH.title" = "Check for Updates";
/* Class = "NSMenuItem"; title = "Always show this menu"; ObjectID = "u5e-3R-3lT"; */
"u5e-3R-3lT.title" = "Always show this menu";
/* Class = "NSMenu"; title = "p≡p updates"; ObjectID = "uQy-DD-JDr"; */
"uQy-DD-JDr.title" = "p≡p updates";
"aUJ-VR-2jH.title" = "Update now";
/* Class = "NSMenuItem"; title = "Keep software up to date"; ObjectID = "y9Y-ru-Qxs"; */
"y9Y-ru-Qxs.title" = "Keep software up to date";

@ -25,7 +25,7 @@
35380AE8247C784E004A08A6 /* pEpUpdater.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pEpUpdater.h; sourceTree = "<group>"; };
35380AE9247C784E004A08A6 /* pEpUpdater.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = pEpUpdater.mm; sourceTree = "<group>"; };
35DC18D2244DA19100FB2002 /* foundation.pEp.adapter.macOS.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = foundation.pEp.adapter.macOS.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
35DC18D5244DA19100FB2002 /* pEpMacOSAdapterProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pEpMacOSAdapterProtocol.h; sourceTree = "<group>"; };
35DC18D5244DA19100FB2002 /* PEPMacOSAdapterProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PEPMacOSAdapterProtocol.h; sourceTree = "<group>"; };
35DC18D6244DA19100FB2002 /* pEpMacOSAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pEpMacOSAdapter.h; sourceTree = "<group>"; };
35DC18D7244DA19100FB2002 /* pEpMacOSAdapter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = pEpMacOSAdapter.m; sourceTree = "<group>"; };
35DC18D9244DA19100FB2002 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@ -65,7 +65,7 @@
isa = PBXGroup;
children = (
35380AE6247BD13A004A08A6 /* README.md */,
35DC18D5244DA19100FB2002 /* pEpMacOSAdapterProtocol.h */,
35DC18D5244DA19100FB2002 /* PEPMacOSAdapterProtocol.h */,
35DC18D6244DA19100FB2002 /* pEpMacOSAdapter.h */,
35DC18D7244DA19100FB2002 /* pEpMacOSAdapter.m */,
35380AE8247C784E004A08A6 /* pEpUpdater.h */,

@ -1,5 +1,5 @@
//
// pEpMacOSAdapterProtocol.h
// PEPMacOSAdapterProtocol.h
// pEpMacOSAdapter
//
// Created by Volker Birk on 20.04.20.
@ -13,7 +13,7 @@
typedef enum { ready = 0, downloading, downloadArrived, noDownloadAvailable } DNType;
/// Callback protocol to notify about download events
@protocol pEpNotificationProtocol
@protocol PEPNotificationProtocol
/// notification about download events
- (void)notifyDownload:(int)type withName:(NSString*)name withFilename:(NSString*)filename;
@ -21,11 +21,11 @@ typedef enum { ready = 0, downloading, downloadArrived, noDownloadAvailable } DN
@end
/// This is the object from the client where we deliver notifications to
@interface pEpNotification : NSObject <pEpNotificationProtocol>
@interface PEPNotification : NSObject <PEPNotificationProtocol>
@end
/// This protocol is providing the XPC interface to the User Interface program pEpNotifications
@protocol pEpMacOSAdapterProtocol
@protocol PEPMacOSAdapterProtocol
/**
subscribe to the published notifications about downloads arriving

@ -31,7 +31,7 @@ pEpUpdater* updater = nil;
// Configure the connection.
// First, set the interface that the exported object implements.
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(pEpMacOSAdapterProtocol)];
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(PEPMacOSAdapterProtocol)];
// Next, set the object that the connection exports. All messages sent on
// the connection to this service will be sent to the exported object to

@ -8,10 +8,10 @@
//
#import <Foundation/Foundation.h>
#import "pEpMacOSAdapterProtocol.h"
#import "PEPMacOSAdapterProtocol.h"
/// This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection.
@interface pEpMacOSAdapter : NSObject <pEpMacOSAdapterProtocol>
@interface pEpMacOSAdapter : NSObject <PEPMacOSAdapterProtocol>
@property (retain) NSXPCConnection* clientConnection;

@ -14,7 +14,7 @@ extern pEpUpdater* updater;
@implementation pEpMacOSAdapter
// begin pEpMacOSAdapterProtocol
// begin PEPMacOSAdapterProtocol
- (void)subscribeForUpdate:(NSXPCListenerEndpoint*)endpoint
{
@ -27,13 +27,13 @@ extern pEpUpdater* updater;
if (updater) {
_clientConnection = [[NSXPCConnection alloc] initWithListenerEndpoint:endpoint];
_clientConnection.remoteObjectInterface = [NSXPCInterface
interfaceWithProtocol:@protocol(pEpNotificationProtocol)];
interfaceWithProtocol:@protocol(PEPNotificationProtocol)];
_clientConnection.interruptionHandler = ^(){NSLog(@"interruption"); updater.subscriber=nil;};
_clientConnection.invalidationHandler = ^(){NSLog(@"invalidation"); updater.subscriber=nil;};
[_clientConnection resume];
pEpNotification* downloadNotification = [_clientConnection remoteObjectProxyWithErrorHandler:^(NSError*err) {
PEPNotification* downloadNotification = [_clientConnection remoteObjectProxyWithErrorHandler:^(NSError*err) {
NSLog(@"%@", err);
}];
@ -68,6 +68,6 @@ extern pEpUpdater* updater;
if (updater) [updater stopUpdates];
}
// end pEpMacOSAdapterProtocol
// end PEPMacOSAdapterProtocol
@end

@ -8,7 +8,7 @@
//
#import <Foundation/Foundation.h>
#import "pEpMacOSAdapterProtocol.h"
#import "PEPMacOSAdapterProtocol.h"
/// object encapsulating the update client functionality of downloadclient
@interface pEpUpdater : NSObject
@ -20,7 +20,7 @@
@property (retain) NSString* configPath;
/// subscriber for notifications
@property (retain) pEpNotification* subscriber;
@property (retain) PEPNotification* subscriber;
/// initializes the pEpUpdater
- (id)init;

Loading…
Cancel
Save