Skip to content

Commit

Permalink
Add Services
Browse files Browse the repository at this point in the history
Adds these services that can be invoked from any application's Services menu, and assigned keyboard shortcuts in the Keyboard panel of System Preferences:

- Start Menubar Countdown
- Stop Menubar Countdown
- Pause Menubar Countdown
- Resume Menubar Countdown
  • Loading branch information
kristopherjohnson committed Oct 31, 2019
1 parent 704327f commit 9e64014
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 22 deletions.
4 changes: 4 additions & 0 deletions MenubarCountdown.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
4EB2044E1BED908300D83EF3 /* Stopwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB2044D1BED908300D83EF3 /* Stopwatch.swift */; };
4EB204501BEE29D700D83EF3 /* AppUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB2044F1BEE29D700D83EF3 /* AppUserDefaults.swift */; };
4EB204541BEE307900D83EF3 /* CALayerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB204531BEE307900D83EF3 /* CALayerExtensions.swift */; };
4EB58AAF236B881D00150BA5 /* ServicesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB58AAE236B881D00150BA5 /* ServicesProvider.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -70,6 +71,7 @@
4EB2044D1BED908300D83EF3 /* Stopwatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stopwatch.swift; sourceTree = "<group>"; };
4EB2044F1BEE29D700D83EF3 /* AppUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUserDefaults.swift; sourceTree = "<group>"; };
4EB204531BEE307900D83EF3 /* CALayerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayerExtensions.swift; sourceTree = "<group>"; };
4EB58AAE236B881D00150BA5 /* ServicesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesProvider.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -163,6 +165,7 @@
4E4865AA1BEFA07E00C159BF /* Image Resources */,
4E4865B01BEFC63700C159BF /* MainMenu.xib */,
4E4D0E552367E5D9004B1404 /* MenubarCountdown.entitlements */,
4EB58AAE236B881D00150BA5 /* ServicesProvider.swift */,
4E4865B11BEFC63700C159BF /* StartTimerDialog.xib */,
4E4865971BEE749E00C159BF /* StartTimerDialogController.swift */,
4EB2044D1BED908300D83EF3 /* Stopwatch.swift */,
Expand Down Expand Up @@ -334,6 +337,7 @@
4EB204501BEE29D700D83EF3 /* AppUserDefaults.swift in Sources */,
4EB2044E1BED908300D83EF3 /* Stopwatch.swift in Sources */,
4E48658E1BEE6CE500C159BF /* Log.swift in Sources */,
4EB58AAF236B881D00150BA5 /* ServicesProvider.swift in Sources */,
4E4865AF1BEFA2B900C159BF /* StringExtensions.swift in Sources */,
4E48659C1BEE81C200C159BF /* TimerExpiredAlertController.swift in Sources */,
4EB204231BED89F900D83EF3 /* AppDelegate.swift in Sources */,
Expand Down
52 changes: 32 additions & 20 deletions MenubarCountdown/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
}

func applicationDidFinishLaunching(_ notification: Notification) {
Log.debug("application did finish launching")
Log.debug("application did finish launching: \(notification)")

UNUserNotificationCenter.current().delegate = self

NSApp.servicesProvider = ServicesProvider(appDelegate: self)

stopwatch.reset()

Expand All @@ -90,8 +94,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
if UserDefaults.standard.bool(forKey: AppUserDefaults.showStartDialogOnLaunchKey) {
showStartTimerDialog(self)
}

UNUserNotificationCenter.current().delegate = self
}

func applicationWillTerminate(_ notification: Notification) {
Expand Down Expand Up @@ -374,7 +376,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
/**
Start the timer.

Called when the user clicks the Start button in the StartTimerDialog.
Called when the user clicks the Start button in the StartTimerDialog
or invokes the Start Countdown service.

If user selected "Show notification", then check for authorization and
show the notification-authorization dialog if necessary before dismissing
Expand Down Expand Up @@ -434,8 +437,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
/**
Reset everything to a not-running state.

Called when the user clicks the Stop menu item or
clicks the OK button in the TimerExpiredAlert.
Called when the user clicks the Stop menu item,
clicks the OK button in the TimerExpiredAlert,
or invokes the Stop Countdown service.
*/
@IBAction func stopTimer(_ sender: AnyObject) {
Log.debug("stop timer")
Expand All @@ -451,35 +455,43 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
/**
Pause the countdown timer.

Called when the user chooses the Pause menu item.
Called when the user chooses the Pause menu item or invokes the Pause Countdown service.
*/
@IBAction func pauseTimer(_ sender: AnyObject) {
Log.debug("pause timer")

isTimerRunning = false
canPause = false
canResume = true
if canPause {
isTimerRunning = false
canPause = false
canResume = true
}
else {
Log.error("can't resume in current state")
}
}

/**
Resume the countdown timer.

Called when the user chooses the Resume menu item.
Called when the user chooses the Resume menu item or invokes the Resume Countdown service.
*/
@IBAction func resumeTimer(_ sender: AnyObject) {
Log.debug("resume timer")
if canResume {
isTimerRunning = true
canPause = true
canResume = false

isTimerRunning = true
canPause = true
canResume = false

timerSettingSeconds = secondsRemaining
timerSettingSeconds = secondsRemaining

stopwatch.reset()
stopwatch.reset()

updateStatusItemTitle(timeRemaining: timerSettingSeconds)
updateStatusItemTitle(timeRemaining: timerSettingSeconds)

waitForNextSecond()
waitForNextSecond()
}
else {
Log.error("can't resume in current state")
}
}

/**
Expand Down
55 changes: 55 additions & 0 deletions MenubarCountdown/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,60 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSServices</key>
<array>
<dict>
<key>NSMessage</key>
<string>startCountdown</string>
<key>NSPortName</key>
<string>Menubar Countdown</string>
<key>NSServiceDescription</key>
<string>Show the Menubar Countdown start dialog</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Start Menubar Countdown</string>
</dict>
</dict>
<dict>
<key>NSMessage</key>
<string>stopCountdown</string>
<key>NSPortName</key>
<string>Menubar Countdown</string>
<key>NSServiceDescription</key>
<string>Stop Menubar Countdown timer</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Stop Menubar Countdown</string>
</dict>
</dict>
<dict>
<key>NSMessage</key>
<string>pauseCountdown</string>
<key>NSPortName</key>
<string>Menubar Countdown</string>
<key>NSServiceDescription</key>
<string>Pause the Menubar Countdown timer</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Pause Menubar Countdown</string>
</dict>
</dict>
<dict>
<key>NSMessage</key>
<string>resumeCountdown</string>
<key>NSPortName</key>
<string>Menubar Countdown</string>
<key>NSServiceDescription</key>
<string>Resume the Menubar Countdown timer when paused</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Resume Menubar Countdown</string>
</dict>
</dict>
</array>
</dict>
</plist>
4 changes: 2 additions & 2 deletions MenubarCountdown/Log.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct Log {
let filename = file.lastPathComponent

let msg = "ERROR: \(message) [\(function) \(filename):\(line)]"
os_log("%{private}@", type: .error, msg)
os_log("%{public}@", type: .error, msg)
}

/**
Expand All @@ -53,7 +53,7 @@ struct Log {

// Note: we log using default level rather than .debug so that the
// messages always go into the log for a debug build
os_log("%{private}@", msg)
os_log("%{public}@", msg)
#endif
}
}
66 changes: 66 additions & 0 deletions MenubarCountdown/ServicesProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// ServicesProvider.swift
// Menubar Countdown
//
// Copyright © 2019 Kristopher Johnson. All rights reserved.
//

import Foundation
import Cocoa

/**
Implements the Services menu items for the application.

The provided services are

- Start Countdown: show the start dialog
- Stop Countdown: reset the timer
- Pause Countdown: pause the timer
- Resume Countdown: resume paused timer

See also

- The `NSServices` entries in `Info.plist`
- Construction and registration of the service provider in `AppDelegate.applicationDidFinishLaunching()`

*/
@objc class ServicesProvider: NSObject {

private var appDelegate: AppDelegate

init(appDelegate: AppDelegate) {
self.appDelegate = appDelegate
}

/**
Handle a Start Countdown service request.
*/
@objc func startCountdown(_ pboard: NSPasteboard, userData: String, error: NSErrorPointer) {
Log.debug("Start Countdown service was requested")
appDelegate.showStartTimerDialog(self)
}

/**
Handle a Stop Countdown service request.
*/
@objc func stopCountdown(_ pboard: NSPasteboard, userData: String, error: NSErrorPointer) {
Log.debug("Stop Countdown service was requested")
appDelegate.stopTimer(self)
}

/**
Handle a Pause Countdown service request.
*/
@objc func pauseCountdown(_ pboard: NSPasteboard, userData: String, error: NSErrorPointer) {
Log.debug("Pause Countdown service was requested")
appDelegate.pauseTimer(self)
}

/**
Handle a Resume Countdown service request.
*/
@objc func resumeCountdown(_ pboard: NSPasteboard, userData: String, error: NSErrorPointer) {
Log.debug("Resume Countdown service was requested")
appDelegate.resumeTimer(self)
}
}

0 comments on commit 9e64014

Please sign in to comment.