diff --git a/PodPals.xcodeproj/project.pbxproj b/PodPals.xcodeproj/project.pbxproj index e2d48ac..e5011d9 100644 --- a/PodPals.xcodeproj/project.pbxproj +++ b/PodPals.xcodeproj/project.pbxproj @@ -334,7 +334,7 @@ CODE_SIGN_ENTITLEMENTS = PodPals/PodPals.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_ASSET_PATHS = "\"PodPals/Preview Content\""; DEVELOPMENT_TEAM = ZC5P42MMZ2; ENABLE_HARDENED_RUNTIME = YES; @@ -365,7 +365,7 @@ CODE_SIGN_ENTITLEMENTS = PodPals/PodPals.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_ASSET_PATHS = "\"PodPals/Preview Content\""; DEVELOPMENT_TEAM = ZC5P42MMZ2; ENABLE_HARDENED_RUNTIME = YES; diff --git a/PodPals/ConnectionCalibrationView.swift b/PodPals/ConnectionCalibrationView.swift index 9416c6c..fdb94ac 100644 --- a/PodPals/ConnectionCalibrationView.swift +++ b/PodPals/ConnectionCalibrationView.swift @@ -8,26 +8,51 @@ enum GestureAction: String, CaseIterable { case volumeDown = "Volume Down" func execute() { + let availability = MediaPlayerChecker.checkAvailability() + switch self { case .playPause: - SpotifyController.shared.playPause() - MusicController.shared.playPause() + if availability == .spotifyOnly || availability == .both { + SpotifyController.shared.playPause() + } + if availability == .appleMusicOnly || availability == .both { + MusicController.shared.playPause() + } + case .nextTrack: - SpotifyController.shared.nextTrack() - MusicController.shared.nextTrack() + if availability == .spotifyOnly || availability == .both { + SpotifyController.shared.nextTrack() + } + if availability == .appleMusicOnly || availability == .both { + MusicController.shared.nextTrack() + } + case .previousTrack: - SpotifyController.shared.previousTrack() - MusicController.shared.previousTrack() + if availability == .spotifyOnly || availability == .both { + SpotifyController.shared.previousTrack() + } + if availability == .appleMusicOnly || availability == .both { + MusicController.shared.previousTrack() + } + case .volumeUp: - SpotifyController.shared.adjustVolume(up: true) - MusicController.shared.adjustVolume(up: true) + if availability == .spotifyOnly || availability == .both { + SpotifyController.shared.adjustVolume(up: true) + } + if availability == .appleMusicOnly || availability == .both { + MusicController.shared.adjustVolume(up: true) + } + case .volumeDown: - SpotifyController.shared.adjustVolume(up: false) - MusicController.shared.adjustVolume(up: false) + if availability == .spotifyOnly || availability == .both { + SpotifyController.shared.adjustVolume(up: false) + } + if availability == .appleMusicOnly || availability == .both { + MusicController.shared.adjustVolume(up: false) + } } } } - class SpotifyController { static let shared = SpotifyController() diff --git a/PodPals/PodPalsApp.swift b/PodPals/PodPalsApp.swift index 8cde278..0539837 100644 --- a/PodPals/PodPalsApp.swift +++ b/PodPals/PodPalsApp.swift @@ -19,6 +19,20 @@ struct PodPalsApp: App { init () { UserDefaults.registerDefaults() + + let availability = MediaPlayerChecker.checkAvailability() + if availability == .neither { + DispatchQueue.main.async { + let alert = NSAlert() + alert.messageText = "No Media Player Available" + alert.informativeText = "PodPals requires either Spotify or Apple Music to be installed and running. Please launch one of these applications to use PodPals." + alert.alertStyle = .critical + alert.addButton(withTitle: "Quit") + + alert.runModal() + NSApplication.shared.terminate(nil) + } + } } var body: some Scene { @@ -68,6 +82,13 @@ class AppState: ObservableObject { } } + @Published var mediaPlayerAvailability: MediaPlayerAvailability { + willSet { + UserDefaults.standard.set(newValue == .both || newValue == .spotifyOnly, forKey: "spotifyAvailable") + UserDefaults.standard.set(newValue == .both || newValue == .appleMusicOnly, forKey: "appleMusicAvailable") + } + } + @AppStorage("AppState.calibration") private var calibration: Data = .init() private var accessCheckTimer = Timer() @@ -82,7 +103,7 @@ class AppState: ObservableObject { self.rightFlick = UserDefaults.standard.string(forKey: "rightFlick") ?? "Next Track" self.nod = UserDefaults.standard.string(forKey: "nod") ?? "Play/Pause" self.trackingEnabled = UserDefaults.standard.bool(forKey: "trackingEnabled") - + self.mediaPlayerAvailability = MediaPlayerChecker.checkAvailability() // Set up motion detector headphoneMotionDetector.onUpdate = { [self] in quaternion = self.headphoneMotionDetector.correctedQuaternion @@ -102,6 +123,7 @@ class AppState: ObservableObject { } } } + } extension UserDefaults { @@ -138,3 +160,58 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + +enum MediaPlayerAvailability { + case spotifyOnly + case appleMusicOnly + case both + case neither +} + +class MediaPlayerChecker { + static func checkAvailability() -> MediaPlayerAvailability { + let spotifyAvailable = checkSpotify() + let appleMusicAvailable = checkAppleMusic() + + switch (spotifyAvailable, appleMusicAvailable) { + case (true, true): + return .both + case (true, false): + return .spotifyOnly + case (false, true): + return .appleMusicOnly + case (false, false): + return .neither + } + } + + private static func checkSpotify() -> Bool { + let script = """ + tell application "System Events" + return exists application process "Spotify" + end tell + """ + + if let scriptObject = NSAppleScript(source: script) { + var error: NSDictionary? + let result = scriptObject.executeAndReturnError(&error) + return result.booleanValue + } + return false + } + + private static func checkAppleMusic() -> Bool { + let script = """ + tell application "System Events" + return exists application process "Music" + end tell + """ + + if let scriptObject = NSAppleScript(source: script) { + var error: NSDictionary? + let result = scriptObject.executeAndReturnError(&error) + return result.booleanValue + } + return false + } +}