diff --git a/AerialUpdater.xcodeproj/project.pbxproj b/AerialUpdater.xcodeproj/project.pbxproj index 96ecb9c..3018636 100644 --- a/AerialUpdater.xcodeproj/project.pbxproj +++ b/AerialUpdater.xcodeproj/project.pbxproj @@ -24,8 +24,8 @@ 03592ABC24CC821500C1AE52 /* Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03592ABB24CC821500C1AE52 /* Update.swift */; }; 03592ABE24CC86E900C1AE52 /* CachedManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03592ABD24CC86E900C1AE52 /* CachedManifest.swift */; }; 035F6F1C24CC8D97002E4EEE /* ErrorLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035F6F1B24CC8D97002E4EEE /* ErrorLog.swift */; }; + 03ADA8EC24E034A40093D0A2 /* LaunchAgent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03ADA8EB24E034A40093D0A2 /* LaunchAgent.swift */; }; 03CE4F5924CB0CA700494D32 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CE4F5824CB0CA700494D32 /* AppDelegate.swift */; }; - 03CE4F5B24CB0CA700494D32 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CE4F5A24CB0CA700494D32 /* ViewController.swift */; }; 03CE4F5D24CB0CA900494D32 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03CE4F5C24CB0CA900494D32 /* Assets.xcassets */; }; 03CE4F6024CB0CA900494D32 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03CE4F5E24CB0CA900494D32 /* Main.storyboard */; }; 03CE4F7E24CB43FE00494D32 /* Manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CE4F7D24CB43FE00494D32 /* Manifest.swift */; }; @@ -51,9 +51,9 @@ 03592ABB24CC821500C1AE52 /* Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update.swift; sourceTree = ""; }; 03592ABD24CC86E900C1AE52 /* CachedManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedManifest.swift; sourceTree = ""; }; 035F6F1B24CC8D97002E4EEE /* ErrorLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLog.swift; sourceTree = ""; }; + 03ADA8EB24E034A40093D0A2 /* LaunchAgent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgent.swift; sourceTree = ""; }; 03CE4F5524CB0CA700494D32 /* AerialUpdater.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AerialUpdater.app; sourceTree = BUILT_PRODUCTS_DIR; }; 03CE4F5824CB0CA700494D32 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 03CE4F5A24CB0CA700494D32 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 03CE4F5C24CB0CA900494D32 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 03CE4F5F24CB0CA900494D32 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 03CE4F6124CB0CA900494D32 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -107,6 +107,7 @@ 0333115A24D85D4600E4622D /* UpdaterVersion.swift */, 03592ABB24CC821500C1AE52 /* Update.swift */, 0319460524CDBD2600F37B35 /* BackgroundCheck.swift */, + 03ADA8EB24E034A40093D0A2 /* LaunchAgent.swift */, ); path = Model; sourceTree = ""; @@ -156,7 +157,6 @@ 03CE4F5E24CB0CA900494D32 /* Main.storyboard */, 03CE4F6124CB0CA900494D32 /* Info.plist */, 03CE4F6224CB0CA900494D32 /* AerialUpdater.entitlements */, - 03CE4F5A24CB0CA700494D32 /* ViewController.swift */, 03592AB624CC80A600C1AE52 /* Model */, 031A3A6924D835B90011E738 /* New Group */, ); @@ -245,13 +245,13 @@ 0333115924D85CCA00E4622D /* String+version.swift in Sources */, 03592AB524CC809F00C1AE52 /* LocalVersion.swift in Sources */, 03592ABE24CC86E900C1AE52 /* CachedManifest.swift in Sources */, - 03CE4F5B24CB0CA700494D32 /* ViewController.swift in Sources */, 03592ABC24CC821500C1AE52 /* Update.swift in Sources */, 0333115624D8448E00E4622D /* MenuViewController.swift in Sources */, 03592AB824CC80FD00C1AE52 /* Helpers.swift in Sources */, 0333115E24D8796F00E4622D /* UpdateCheckWindowController.swift in Sources */, 03592AB224CC765500C1AE52 /* Preferences.swift in Sources */, 03CE4F5924CB0CA700494D32 /* AppDelegate.swift in Sources */, + 03ADA8EC24E034A40093D0A2 /* LaunchAgent.swift in Sources */, 03CE4F8024CB4CFB00494D32 /* FileDownloader.swift in Sources */, 035F6F1C24CC8D97002E4EEE /* ErrorLog.swift in Sources */, ); diff --git a/AerialUpdater/AppDelegate.swift b/AerialUpdater/AppDelegate.swift index aefa662..2462b34 100644 --- a/AerialUpdater/AppDelegate.swift +++ b/AerialUpdater/AppDelegate.swift @@ -19,12 +19,68 @@ class AppDelegate: NSObject, NSApplicationDelegate { // MARK: - Lifecycle func applicationDidFinishLaunching(_ aNotification: Notification) { - debugLog("Version \(Helpers.version) launched") + debugLog("Version \(Helpers.version) launched on \(ProcessInfo.processInfo.operatingSystemVersionString)") - // TODO detect launch params + let arguments = ProcessInfo.processInfo.arguments + + // This is imperative, breaks everything + ensureNotInstalledForAllUsers() + + if arguments.contains("--silent") { + debugLog("Background mode") + + // Returns true if we need to stay around + if !silentModeCheck() { + return + } + // We're staying then ! + debugLog("Falling back to menu for a notification") + } + + // Menu mode, we may fall down here from silent mode too in notify mode, + // or if we must update + + // Set the icon + setIcon(mode: .normal) + createMenu() + } + + // This returns true if we should stay around, and false if we can safely return + func silentModeCheck() -> Bool { + Update.instance.commandLine = true + + // Force a cache update + CachedManifest.instance.updateNow() + + // Make sure we don't need to update, or redirect you there + if UpdaterVersion.needsUpdating() { + return true // Then stay around ! + } - // Ensure we don't have something in /Library/Screen Savers/ + let (stringVersion, shouldInstall) = Update.instance.check() + + if shouldInstall { + debugLog(stringVersion) + if Preferences.updateMode == .automatic { + Update.instance.unattendedPerform() + return false // We are done + } else { + return true // Stay around ! + } + } else { + // We need to stay around for a bit, because if not + // launchd will think we are crashing... + debugLog("No new version, quitting in 20sec.") + RunLoop.main.run(until: Date() + 0x14) + NSApplication.shared.terminate(self) + + return false + } + } + + // Ensure we don't have something in /Library/Screen Savers/ + func ensureNotInstalledForAllUsers() { if LocalVersion.isInstalledForAllUsers() { NSApp.activate(ignoringOtherApps: true) // Open finder with the file selected @@ -39,14 +95,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } } - - // Menu mode - - // Set the icon - setIcon(mode: .normal) - - createMenu() - } func applicationWillTerminate(_ aNotification: Notification) { @@ -56,14 +104,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { // Change the icon based on status func setIcon(mode: IconMode) { DispatchQueue.main.async { - print("setIcon") + print("setIcon \(mode)") switch mode { case .normal: self.statusItem.image = NSImage(named: "Status48") case .updating: self.statusItem.image = NSImage(named: "StatusTransp48") case .notification: - self.statusItem.image = NSImage(named: "StatusGreen48") + self.statusItem.image = NSImage(named: "Status48Attention") } self.statusItem.image?.size.width = 22 diff --git a/AerialUpdater/Assets.xcassets/Status48Attention.imageset/Contents.json b/AerialUpdater/Assets.xcassets/Status48Attention.imageset/Contents.json new file mode 100644 index 0000000..7442507 --- /dev/null +++ b/AerialUpdater/Assets.xcassets/Status48Attention.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Status48Attention@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AerialUpdater/Assets.xcassets/Status48Attention.imageset/Status48Attention@2x.png b/AerialUpdater/Assets.xcassets/Status48Attention.imageset/Status48Attention@2x.png new file mode 100644 index 0000000..202c3ad Binary files /dev/null and b/AerialUpdater/Assets.xcassets/Status48Attention.imageset/Status48Attention@2x.png differ diff --git a/AerialUpdater/Model/BackgroundCheck.swift b/AerialUpdater/Model/BackgroundCheck.swift index f31b59c..e553be3 100644 --- a/AerialUpdater/Model/BackgroundCheck.swift +++ b/AerialUpdater/Model/BackgroundCheck.swift @@ -45,4 +45,14 @@ class BackgroundCheck { } } + func getTimer() -> Int { + switch Preferences.checkEvery { + case .hour: + return hourSeconds + case .day: + return daySeconds + case .week: + return weekSeconds + } + } } diff --git a/AerialUpdater/Model/Helpers/ErrorLog.swift b/AerialUpdater/Model/Helpers/ErrorLog.swift index cbe36b4..2e161b1 100644 --- a/AerialUpdater/Model/Helpers/ErrorLog.swift +++ b/AerialUpdater/Model/Helpers/ErrorLog.swift @@ -50,7 +50,7 @@ func Log(level: ErrorLevel, message: String) { } func logToDisk(_ message: String) { - DispatchQueue.main.async { + //DispatchQueue.main.async { // Prefix message with date let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" @@ -67,9 +67,10 @@ func logToDisk(_ message: String) { let fileHandle = try FileHandle(forWritingTo: cacheFileUrl) fileHandle.seekToEndOfFile() fileHandle.write(data) + fileHandle.synchronizeFile() fileHandle.closeFile() } catch { - NSLog("AerialUpdater: Can't open handle for Log.txt") + NSLog("AerialUpdater: Can't open handle for Log.txt \(error.localizedDescription)") } } else { // Create new log @@ -79,7 +80,7 @@ func logToDisk(_ message: String) { NSLog("AerialUpdater: Can't write to Log.txt") } } - } + //} } func debugLog(_ message: String) { diff --git a/AerialUpdater/Model/Helpers/Helpers.swift b/AerialUpdater/Model/Helpers/Helpers.swift index 1fd946b..3062568 100644 --- a/AerialUpdater/Model/Helpers/Helpers.swift +++ b/AerialUpdater/Model/Helpers/Helpers.swift @@ -8,6 +8,16 @@ import Cocoa struct Helpers { + static func showErrorAlert(question: String, text: String, button: String = "OK") { + let alert = NSAlert() + alert.messageText = question + alert.informativeText = text + alert.alertStyle = .critical + alert.icon = NSImage(named: NSImage.cautionName) + alert.addButton(withTitle: button) + alert.runModal() + } + static func showAlert(question: String, text: String, button1: String = "OK", button2: String = "Cancel") -> Bool { let alert = NSAlert() alert.messageText = question diff --git a/AerialUpdater/Model/LaunchAgent.swift b/AerialUpdater/Model/LaunchAgent.swift new file mode 100644 index 0000000..a3a3f37 --- /dev/null +++ b/AerialUpdater/Model/LaunchAgent.swift @@ -0,0 +1,155 @@ +// +// LaunchAgent.swift +// AerialUpdater +// +// Created by Guillaume Louel on 09/08/2020. +// + +import Cocoa + +struct LaunchAgent { + // com.glouel.AerialUpdaterAgent.plist + static let agentPath: String = { + let path = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)[0] as String + let url = NSURL(fileURLWithPath: path) + if let pathComponent = url.appendingPathComponent("LaunchAgents/com.glouel.AerialUpdaterAgent.plist") { + return pathComponent.path + } else { + return "" + } + }() + + /// Update the Launch Agent to the current settings + /// + /// It will remove the Launch Agent first if any exists then install the correct one depending on the mode. In manual mode, nothing is installed. + static public func update() { + // Let's start by cleaning up + removeAgent() + + // If we need to set something, do it + if Preferences.launchMode != .manual { + installAgent() + } + } + + static private func removeAgent() { + if FileManager.default.fileExists(atPath: agentPath) { + do { + debugLog("Removing LaunchAgent") + try FileManager().removeItem(atPath: agentPath) + } catch { + // ERROR + errorLog("Cannot remove LaunchAgent") + Helpers.showErrorAlert(question: "Cannot remove launch agent", text: "Please report !") + return + } + } + } + + static private func installAgent() { + var agent: String = "" + switch Preferences.launchMode { + case .manual: + errorLog("Calling installAgent in manual mode, please report") + return + case .startup: + agent = getStartupAgent() + case .background: + agent = getBackgroundAgent() + } + + do { + try agent.write(toFile: agentPath, atomically: true, encoding: .utf8) + debugLog("LaunchAgent installed") + } catch { + Helpers.showErrorAlert(question: "Can't install LaunchAgent", text: "Please report the issue") + } + + restart() + if Preferences.launchMode == .background { + debugLog("Quitting after installing background mode") + NSApplication.shared.terminate(nil) + } + } + + // Restarts our agent via launchctl + // This may not work on very old macOS versions? + static private func restart() { + debugLog("Restarting LaunchAgent") + let out1 = Helpers.shell(launchPath: "/bin/launchctl", arguments: ["unload", agentPath]) + debugLog(out1 ?? "(no output)") + let out2 = Helpers.shell(launchPath: "/bin/launchctl", arguments: ["load", agentPath]) + debugLog(out2 ?? "(no output)") + } + + static private func getStartupAgent() -> String { + let top = +""" + + + + + Label + com.glouel.AerialUpdater + LimitLoadToSessionType + Aqua + ProgramArguments + + /usr/bin/open +""" + + let bundleLine = "" + Bundle.main.bundlePath + "" + + let bottom = +""" + + + RunAtLoad + + + +""" + return top + bundleLine + bottom + } + + + static private func getBackgroundAgent() -> String { + let top = +""" + + + + + Label + com.glouel.AerialUpdater + LimitLoadToSessionType + Aqua + ProgramArguments + + /usr/bin/open +""" + + let bundleLine = "" + Bundle.main.bundlePath + "" + + let bottom = +""" + + --args + --silent + + RunAtLoad + + StartInterval + +""" + let bottom2 = + """ + + + +""" + return top + bundleLine + bottom + String(BackgroundCheck.instance.getTimer()) + bottom2 + } + +} + diff --git a/AerialUpdater/Model/Manifest/Manifest.swift b/AerialUpdater/Model/Manifest/Manifest.swift index 8176494..7e39160 100644 --- a/AerialUpdater/Model/Manifest/Manifest.swift +++ b/AerialUpdater/Model/Manifest/Manifest.swift @@ -9,8 +9,9 @@ import Foundation // MARK: - Manifest struct Manifest: Codable { - let updaterVersion, alphaVersion, alphaSHA256, betaVersion: String - let betaSHA256, releaseVersion, releaseSHA256: String + let alphaVersion, alphaSHA256: String? // This will go away soon + let updaterVersion, betaVersion, betaSHA256: String + let releaseVersion, releaseSHA256: String } // MARK: Manifest convenience initializers and mutators @@ -31,7 +32,7 @@ extension Manifest { try self.init(data: try Data(contentsOf: url)) } - func with( +/* func with( updaterVersion: String? = nil, alphaVersion: String? = nil, alphaSHA256: String? = nil, @@ -49,7 +50,7 @@ extension Manifest { releaseVersion: releaseVersion ?? self.releaseVersion, releaseSHA256: releaseSHA256 ?? self.releaseSHA256 ) - } + }*/ func jsonData() throws -> Data { return try newJSONEncoder().encode(self) diff --git a/AerialUpdater/Model/Preferences.swift b/AerialUpdater/Model/Preferences.swift index 410608e..59737ab 100644 --- a/AerialUpdater/Model/Preferences.swift +++ b/AerialUpdater/Model/Preferences.swift @@ -19,6 +19,10 @@ enum CheckEvery: Int, Codable { case hour, day, week } +enum LaunchMode: Int, Codable { + case manual, startup, background +} + struct Preferences { // Which version are we looking for ? Defaults to release @SimpleStorage(key: "intDesiredVersion", defaultValue: DesiredVersion.release.rawValue) @@ -66,14 +70,23 @@ struct Preferences { } } + // Automatic or notifications ? + @SimpleStorage(key: "intLaunchMode", defaultValue: LaunchMode.manual.rawValue) + static var intLaunchMode: Int + + // We wrap in a separate value, as we can't store an enum as a Codable in + // macOS < 10.15 + static var launchMode: LaunchMode { + get { + return LaunchMode(rawValue: intLaunchMode)! + } + set(value) { + intLaunchMode = value.rawValue + } + } + @SimpleStorage(key: "debugMode", defaultValue: false) static var debugMode: Bool - - @SimpleStorage(key: "launchAtStartup", defaultValue: false) - static var launchAtStartup: Bool - - @SimpleStorage(key: "launchInBackground", defaultValue: false) - static var launchInBackground: Bool } diff --git a/AerialUpdater/Model/Update.swift b/AerialUpdater/Model/Update.swift index 7e74419..fb59e4d 100644 --- a/AerialUpdater/Model/Update.swift +++ b/AerialUpdater/Model/Update.swift @@ -5,7 +5,7 @@ // Created by Guillaume Louel on 25/07/2020. // -import Foundation +import Cocoa protocol UpdateCallback { func updateProgress(string: String, done: Bool) @@ -16,10 +16,11 @@ protocol UpdateCallback { // Again this is a bit messy... class Update { static let instance: Update = Update() - + var uiCallback: UpdateCallback? var shouldReport = false - + var commandLine = false + func setCallback(_ cb: UpdateCallback) { uiCallback = cb } @@ -55,7 +56,7 @@ class Update { cb.setIcon(mode: .notification) } } - } + } } } @@ -68,7 +69,7 @@ class Update { if let manifest = CachedManifest.instance.manifest { let localVersion = LocalVersion.get() - debugLog("Versions: local \(localVersion), alpha \(manifest.alphaVersion), beta \(manifest.betaVersion), release \(manifest.releaseVersion)") + debugLog("Versions: local \(localVersion), beta \(manifest.betaVersion), release \(manifest.releaseVersion)") switch Preferences.desiredVersion { case .beta: @@ -113,6 +114,15 @@ class Update { cb.setIcon(mode: .normal) cb.updateMenuContent() } + + if commandLine { + // We quit here + DispatchQueue.main.async { + debugLog("Update process done, quitting in 20sec.") + RunLoop.main.run(until: Date() + 0x14) + NSApplication.shared.terminate(self) + } + } } } @@ -165,7 +175,7 @@ class Update { switch Preferences.desiredVersion { case .beta: - zipPath = "https://github.com/JohnCoates/Aerial/relefases/download/v\(manifest.betaVersion)/Aerial.saver.zip" + zipPath = "https://github.com/JohnCoates/Aerial/releases/download/v\(manifest.betaVersion)/Aerial.saver.zip" case .release: zipPath = "https://github.com/JohnCoates/Aerial/releases/download/v\(manifest.releaseVersion)/Aerial.saver.zip" } @@ -289,6 +299,8 @@ class Update { do { try FileManager.default.moveItem(atPath: path, toPath: LocalVersion.aerialPath) + + debugLog("Installed!") return true } catch { return false diff --git a/AerialUpdater/UI/MenuView.xib b/AerialUpdater/UI/MenuView.xib index 7090085..d4330ec 100644 --- a/AerialUpdater/UI/MenuView.xib +++ b/AerialUpdater/UI/MenuView.xib @@ -16,6 +16,7 @@ + @@ -121,27 +122,39 @@ - + - + - + - + - - + - + - + - + + + + + + + + + + + + + + diff --git a/AerialUpdater/UI/MenuViewController.swift b/AerialUpdater/UI/MenuViewController.swift index d813b77..84c207d 100644 --- a/AerialUpdater/UI/MenuViewController.swift +++ b/AerialUpdater/UI/MenuViewController.swift @@ -34,6 +34,7 @@ class MenuViewController: NSViewController, UpdateCallback { // Settings + @IBOutlet var menuLaunchManually: NSMenuItem! @IBOutlet var menuLaunchAtStartup: NSMenuItem! @IBOutlet var menuLaunchInBackground: NSMenuItem! @@ -57,9 +58,8 @@ class MenuViewController: NSViewController, UpdateCallback { updateMenuSettings() updateMenuContent() - // Then set the callback - //BackgroundCheck.instance.set() + BackgroundCheck.instance.set() } func setDelegate(_ delegate: AppDelegate) { @@ -92,6 +92,15 @@ class MenuViewController: NSViewController, UpdateCallback { menuWeek.state = .on } + switch Preferences.launchMode { + case .manual: + menuLaunchManually.state = .on + case .startup: + menuLaunchAtStartup.state = .on + case .background: + menuLaunchInBackground.state = .on + } + menuDebugMode.state = Preferences.debugMode ? .on : .off } @@ -105,9 +114,10 @@ class MenuViewController: NSViewController, UpdateCallback { // We may need to update AerialUpdater. This will be done as infrequently as possible, only in case of a security issue (not for features) if UpdaterVersion.needsUpdating() { + self.setIcon(mode: .notification) print(manifest.updaterVersion) print(Helpers.version) - self.versionLabel.stringValue = "New AerialUpdater" + self.versionLabel.stringValue = "New updater version" self.versionImageView.isHidden = true self.versionInstallNow.isHidden = false self.goodTrick.isHidden = true @@ -122,10 +132,13 @@ class MenuViewController: NSViewController, UpdateCallback { if shouldInstall { // Make the button visible + self.setIcon(mode: .notification) + self.versionImageView.isHidden = true self.versionInstallNow.isHidden = false } else { // Make the button visible + self.setIcon(mode: .normal) self.versionImageView.isHidden = false self.versionInstallNow.isHidden = true } @@ -143,6 +156,8 @@ class MenuViewController: NSViewController, UpdateCallback { if string == "OK" { self.updateMenuContent() + } else { + Helpers.showErrorAlert(question: "Installation error", text: "\(string) \n\nPlease report.)") } } else { self.versionLabel.stringValue = string @@ -235,7 +250,35 @@ class MenuViewController: NSViewController, UpdateCallback { updateCheckWindowController.startCheck() } - // MARK: - Settings + // MARK: - Launch + + /// Handle the launch submenu + @IBAction func launchModeChange(_ sender: NSMenuItem) { + if sender == menuLaunchInBackground { + // Let's make sure they understand + if !Helpers.showAlert(question: "Background mode", text: "By enabling background mode, Aerial Updater will shedule itself to run for a few seconds at the frequency you selected.\n\nThe updater will quit the menu so make sure you change your other settings first. Know you can launch the updater manually at any time to change any setting.", button1: "Go in the background and Quit", button2: "Cancel") { + return + } + } + + sender.state = .on + if sender == menuLaunchManually { + menuLaunchAtStartup.state = .off + menuLaunchInBackground.state = .off + Preferences.launchMode = .manual + } else if sender == menuLaunchAtStartup { + menuLaunchManually.state = .off + menuLaunchAtStartup.state = .off + Preferences.launchMode = .startup + } else if sender == menuLaunchInBackground { + menuLaunchManually.state = .off + menuLaunchInBackground.state = .off + Preferences.launchMode = .background + } + + LaunchAgent.update() + } + /// Show About Updater version window @IBAction func aboutUpdater(_ sender: NSMenuItem) { @@ -251,17 +294,6 @@ class MenuViewController: NSViewController, UpdateCallback { NSApp.activate(ignoringOtherApps: true) } - @IBAction func launchAtStartup(_ sender: NSMenuItem) { - Preferences.launchAtStartup = !Preferences.launchAtStartup - - sender.state = Preferences.launchAtStartup ? .on : .off - } - - @IBAction func launchInBackground(_ sender: NSMenuItem) { - Preferences.launchInBackground = !Preferences.launchInBackground - - sender.state = Preferences.launchInBackground ? .on : .off - } @IBAction func debugMode(_ sender: Any) { Preferences.debugMode = !Preferences.debugMode diff --git a/AerialUpdater/UI/UpdateCheckWindowController.swift b/AerialUpdater/UI/UpdateCheckWindowController.swift index 6abfda2..2d7f3eb 100644 --- a/AerialUpdater/UI/UpdateCheckWindowController.swift +++ b/AerialUpdater/UI/UpdateCheckWindowController.swift @@ -30,7 +30,7 @@ class UpdateCheckWindowController: actionButton.isHidden = true actionButton.isHighlighted = true // Color our button } - + func setCallback(_ cb: MenuViewController) { menuCallback = cb } @@ -127,6 +127,7 @@ class UpdateCheckWindowController: self.progressLabel.stringValue = "Update successfully completed" } else { self.setProgress(to: .warning) + Helpers.showErrorAlert(question: "Installation error", text: "\(string)\n\nPlease report.\n\nPlease report.") } self.actionButton.isHidden = false diff --git a/AerialUpdater/UI/UpdateCheckWindowController.xib b/AerialUpdater/UI/UpdateCheckWindowController.xib index 6131b9e..a2e6916 100644 --- a/AerialUpdater/UI/UpdateCheckWindowController.xib +++ b/AerialUpdater/UI/UpdateCheckWindowController.xib @@ -25,7 +25,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -87,6 +87,7 @@ Gw + diff --git a/AerialUpdater/ViewController.swift b/AerialUpdater/ViewController.swift deleted file mode 100644 index cc21b4f..0000000 --- a/AerialUpdater/ViewController.swift +++ /dev/null @@ -1,109 +0,0 @@ -// -// ViewController.swift -// AerialUpdater -// -// Created by Guillaume Louel on 24/07/2020. -// - -import Cocoa - -enum DesiredType: Int { - case alpha, beta, release -} -class ViewController: NSViewController { - var manifest: Manifest? - var desiredVersion: DesiredType = .release - @IBOutlet var currentlyInstalledLabel: NSTextField! - @IBOutlet var latestAvailableLabel: NSTextField! - @IBOutlet var installNowButton: NSButton! - @IBOutlet var desiredVersionPopup: NSPopUpButton! - - @IBOutlet var spinner: NSProgressIndicator! - @IBOutlet var progressLabel: NSTextField! - - override func viewDidLoad() { - super.viewDidLoad() - - //manifest = VersionChecker.getManifest() - - //currentlyInstalledLabel.stringValue = VersionChecker.getAerialVersion() - desiredVersionPopup.selectItem(at: desiredVersion.rawValue) - installNowButton.isEnabled = false - - spinner.isHidden = true - progressLabel.isHidden = true - - updateVersionLabel() - } - - override var representedObject: Any? { - didSet { - // Update the view, if already loaded. - } - } - - @IBAction func desiredVersionPopupChange(_ sender: NSPopUpButton) { - //currentlyInstalledLabel.stringValue = VersionChecker.getAerialVersion() - - switch sender.indexOfSelectedItem { - case 0: // Alpha - desiredVersion = .alpha - case 1: // Beta - desiredVersion = .beta - default: // Release - desiredVersion = .release - } - - updateVersionLabel() - } - - func updateVersionLabel() { - switch desiredVersion { - case .alpha: - if let manifest = manifest { - latestAvailableLabel.stringValue = manifest.alphaVersion - } - case .beta: - if let manifest = manifest { - latestAvailableLabel.stringValue = manifest.betaVersion - } - case .release: - if let manifest = manifest { - latestAvailableLabel.stringValue = manifest.releaseVersion - } - } - - if (latestAvailableLabel.stringValue != currentlyInstalledLabel.stringValue) { - installNowButton.isEnabled = true - } else { - installNowButton.isEnabled = false - } - } - - @IBAction func installNowButtonClick(_ sender: Any) { - if let manifest = manifest { - installNowButton.isEnabled = false - spinner.isHidden = false - spinner.startAnimation(self) - progressLabel.isHidden = false - - //VersionChecker.updateTo(desired: desiredVersion, manifest: manifest, vc: self) - } - } - - func updateProgress(string: String, done: Bool) { - DispatchQueue.main.async { - if done { - self.progressLabel.stringValue = string - self.spinner.stopAnimation(self) - self.spinner.isHidden = true - - //self.currentlyInstalledLabel.stringValue = VersionChecker.getAerialVersion() - } else { - self.progressLabel.stringValue = string - } - } - } - -} -