diff --git a/Underbar.xcodeproj/project.pbxproj b/Underbar.xcodeproj/project.pbxproj index fc461f0..842650f 100644 --- a/Underbar.xcodeproj/project.pbxproj +++ b/Underbar.xcodeproj/project.pbxproj @@ -14,8 +14,8 @@ D6B35B4E22DA479E00F262A2 /* TouchBarItemIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B35B4D22DA479E00F262A2 /* TouchBarItemIdentifiers.swift */; }; D6B35B5122DA4A2A00F262A2 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6B35B5022DA4A2A00F262A2 /* DFRFoundation.framework */; }; D6B35B5522DA64AD00F262A2 /* RootTouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B35B5422DA64AD00F262A2 /* RootTouchBarController.swift */; }; - D6B35B5A22DA66A400F262A2 /* TouchBarWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B35B5922DA66A400F262A2 /* TouchBarWidget.swift */; }; - D6B35B5E22DA687E00F262A2 /* NowPlayingWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B35B5D22DA687E00F262A2 /* NowPlayingWidget.swift */; }; + D6B35B6222DA6B2100F262A2 /* NowPlayingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B35B6122DA6B2100F262A2 /* NowPlayingItem.swift */; }; + D6B35B6522DA6ECC00F262A2 /* MediaRemote.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6B35B6422DA6ECC00F262A2 /* MediaRemote.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -31,8 +31,9 @@ D6B35B4D22DA479E00F262A2 /* TouchBarItemIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarItemIdentifiers.swift; sourceTree = ""; }; D6B35B5022DA4A2A00F262A2 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = ""; }; D6B35B5422DA64AD00F262A2 /* RootTouchBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootTouchBarController.swift; sourceTree = ""; }; - D6B35B5922DA66A400F262A2 /* TouchBarWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarWidget.swift; sourceTree = ""; }; - D6B35B5D22DA687E00F262A2 /* NowPlayingWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingWidget.swift; sourceTree = ""; }; + D6B35B6122DA6B2100F262A2 /* NowPlayingItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingItem.swift; sourceTree = ""; }; + D6B35B6322DA6E5700F262A2 /* MRMediaRemote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MRMediaRemote.h; sourceTree = ""; }; + D6B35B6422DA6ECC00F262A2 /* MediaRemote.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaRemote.framework; path = ../../../../System/Library/PrivateFrameworks/MediaRemote.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -41,6 +42,7 @@ buildActionMask = 2147483647; files = ( D6B35B5122DA4A2A00F262A2 /* DFRFoundation.framework in Frameworks */, + D6B35B6522DA6ECC00F262A2 /* MediaRemote.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -67,16 +69,16 @@ D6B35B3722DA1B3C00F262A2 /* Underbar */ = { isa = PBXGroup; children = ( + D6B35B4A22DA1C8400F262A2 /* Underbar-Bridging-Header.h */, D6B35B3822DA1B3C00F262A2 /* AppDelegate.swift */, D6B35B4D22DA479E00F262A2 /* TouchBarItemIdentifiers.swift */, + D6B35B5F22DA6AD500F262A2 /* Private */, D6B35B5722DA669000F262A2 /* Controllers */, - D6B35B5822DA669400F262A2 /* Widgets */, + D6B35B6022DA6B0400F262A2 /* Items */, D6B35B3C22DA1B3C00F262A2 /* Assets.xcassets */, D6B35B3E22DA1B3C00F262A2 /* Main.storyboard */, D6B35B4122DA1B3C00F262A2 /* Info.plist */, D6B35B4222DA1B3C00F262A2 /* Underbar.entitlements */, - D6B35B4822DA1B8D00F262A2 /* NSTouchBar.h */, - D6B35B4A22DA1C8400F262A2 /* Underbar-Bridging-Header.h */, ); path = Underbar; sourceTree = ""; @@ -84,6 +86,7 @@ D6B35B4F22DA4A2A00F262A2 /* Frameworks */ = { isa = PBXGroup; children = ( + D6B35B6422DA6ECC00F262A2 /* MediaRemote.framework */, D6B35B5022DA4A2A00F262A2 /* DFRFoundation.framework */, ); name = Frameworks; @@ -98,13 +101,21 @@ path = Controllers; sourceTree = ""; }; - D6B35B5822DA669400F262A2 /* Widgets */ = { + D6B35B5F22DA6AD500F262A2 /* Private */ = { isa = PBXGroup; children = ( - D6B35B5922DA66A400F262A2 /* TouchBarWidget.swift */, - D6B35B5D22DA687E00F262A2 /* NowPlayingWidget.swift */, + D6B35B4822DA1B8D00F262A2 /* NSTouchBar.h */, + D6B35B6322DA6E5700F262A2 /* MRMediaRemote.h */, ); - path = Widgets; + path = Private; + sourceTree = ""; + }; + D6B35B6022DA6B0400F262A2 /* Items */ = { + isa = PBXGroup; + children = ( + D6B35B6122DA6B2100F262A2 /* NowPlayingItem.swift */, + ); + path = Items; sourceTree = ""; }; /* End PBXGroup section */ @@ -177,10 +188,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D6B35B5A22DA66A400F262A2 /* TouchBarWidget.swift in Sources */, D6B35B4E22DA479E00F262A2 /* TouchBarItemIdentifiers.swift in Sources */, D6B35B4C22DA466600F262A2 /* TouchBarController.swift in Sources */, - D6B35B5E22DA687E00F262A2 /* NowPlayingWidget.swift in Sources */, + D6B35B6222DA6B2100F262A2 /* NowPlayingItem.swift in Sources */, D6B35B3922DA1B3C00F262A2 /* AppDelegate.swift in Sources */, D6B35B5522DA64AD00F262A2 /* RootTouchBarController.swift in Sources */, ); diff --git a/Underbar/Controllers/RootTouchBarController.swift b/Underbar/Controllers/RootTouchBarController.swift index 70218cb..54cdbfa 100644 --- a/Underbar/Controllers/RootTouchBarController.swift +++ b/Underbar/Controllers/RootTouchBarController.swift @@ -10,15 +10,11 @@ import Cocoa class RootTouchBarController: TouchBarController { - let nowPlaying = NowPlayingWidget() + let nowPlaying = NowPlayingItem(identifier: .nowPlaying) override init() { super.init() - widgets = [ - nowPlaying - ] - touchBar.defaultItemIdentifiers = [ .closeUnderbar, .flexibleSpace, @@ -31,7 +27,7 @@ class RootTouchBarController: TouchBarController { case .closeUnderbar: return createCloseItem() case .nowPlaying: - return nowPlaying.item + return nowPlaying default: return super.touchBar(touchBar, makeItemForIdentifier: identifier) } diff --git a/Underbar/Controllers/TouchBarController.swift b/Underbar/Controllers/TouchBarController.swift index 3ede5b1..814325b 100644 --- a/Underbar/Controllers/TouchBarController.swift +++ b/Underbar/Controllers/TouchBarController.swift @@ -11,8 +11,6 @@ import Cocoa class TouchBarController: NSObject, NSTouchBarDelegate { let touchBar = NSTouchBar() - var widgets: [TouchBarWidget] = [] - var isVisible = false override init() { diff --git a/Underbar/Items/NowPlayingItem.swift b/Underbar/Items/NowPlayingItem.swift new file mode 100644 index 0000000..b7b4597 --- /dev/null +++ b/Underbar/Items/NowPlayingItem.swift @@ -0,0 +1,127 @@ +// +// NowPlayingItem.swift +// Underbar +// +// Created by Shadowfacts on 7/13/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import Cocoa +import MediaToolbox + +class NowPlayingItem: NSCustomTouchBarItem { + + var nowPlayingTitle: String? + var nowPlayingArtist: String? + var nowPlayingAlbum: String? + var nowPlayingArtwork: Data? + + var button: NSButton! + + var lastPressTime: Date? = nil + var multiPressCount: Int = 0 + var multiPressWorkItem: DispatchWorkItem? = nil + + override init(identifier: NSTouchBarItem.Identifier) { + super.init(identifier: identifier) + + button = NSButton(title: "Test\nline 2", target: self, action: #selector(buttonPressed)) + button.alignment = .left + + button.widthAnchor.constraint(equalToConstant: 200).isActive = true + + view = button + + MRMediaRemoteRegisterForNowPlayingNotifications(.global(qos: .utility)) + registerForNotifications() + + updateMediaInfo() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func registerForNotifications() { + NotificationCenter.default.addObserver(self, selector: #selector(updateMediaInfo), name: .init(kMRMediaRemoteNowPlayingApplicationClientStateDidChange), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(updateMediaInfo), name: .mrNowPlayingPlaybackQueueChanged, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(updateMediaInfo), name: .mrNowPlayingPlaybackQueueChanged, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(updateMediaInfo), name: .mrMediaRemoteNowPlayingApplicationIsPlayingDidChange, object: nil) + } + + @objc func updateMediaInfo() { + MRMediaRemoteGetNowPlayingInfo(.main) { [weak self] (info) in + guard let self = self, let info = info else { return } + + self.nowPlayingTitle = info[kMRMediaRemoteNowPlayingInfoTitle] as? String + self.nowPlayingArtist = info[kMRMediaRemoteNowPlayingInfoArtist] as? String + self.nowPlayingAlbum = info[kMRMediaRemoteNowPlayingInfoAlbum] as? String + self.nowPlayingArtwork = info[kMRMediaRemoteNowPlayingInfoArtworkData] as? Data + + self.updateButtonTitle() + self.updateButtonImage() + } + } + + func updateButtonTitle() { + let attributedStr = NSMutableAttributedString() + + + + let line1 = NSAttributedString(string: nowPlayingTitle ?? "Unknown") + attributedStr.append(line1) + attributedStr.setAttributes([.font: NSFont.systemFont(ofSize: 12)], range: NSRange(location: 0, length: line1.length)) + + attributedStr.append(NSAttributedString(string: "\n")) + + let line2 = NSAttributedString(string: (nowPlayingArtist ?? "Unknown") + " | " + (nowPlayingAlbum ?? "Unknown")) + attributedStr.append(line2) + attributedStr.setAttributes([.font: NSFont.systemFont(ofSize: 10)], range: NSRange(location: line1.length + 1, length: line2.length)) + + button.attributedTitle = attributedStr + } + + func updateButtonImage() { + if let data = nowPlayingArtwork, + let image = NSImage(data: data) { + button.image = image + button.imagePosition = .imageLeft + } else { + button.image = nil + } + } + + @objc func buttonPressed() { + if let lastPressTime = lastPressTime, + lastPressTime.timeIntervalSinceNow < 0.2 { + multiPressCount += 1 + multiPressWorkItem?.cancel() + } else { + self.lastPressTime = Date() + multiPressCount = 1 + } + + multiPressWorkItem = DispatchWorkItem { + self.handleMultiPress(presses: self.multiPressCount) + self.lastPressTime = nil + self.multiPressCount = 0 + self.multiPressWorkItem = nil + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: multiPressWorkItem!) + + } + + func handleMultiPress(presses: Int) { + switch presses { + case 1: + MRMediaRemoteSendCommand(kMRTogglePlayPause, nil) + case 2: + MRMediaRemoteSendCommand(kMRNextTrack, nil) + case 3: + MRMediaRemoteSendCommand(kMRPreviousTrack, nil) + default: + break + } + } + +} diff --git a/Underbar/Private/MRMediaRemote.h b/Underbar/Private/MRMediaRemote.h new file mode 100644 index 0000000..20f7baf --- /dev/null +++ b/Underbar/Private/MRMediaRemote.h @@ -0,0 +1,43 @@ +// +// MRMediaRemote.h +// Underbar +// +// Created by Shadowfacts on 7/13/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +// Copied from https://github.com/pigigaldi/Pock/blob/852679a4882d0e9c5b3d8473eb9b9fbf7bef837f/Pock/Private/MRMediaRemote.h + +typedef void (^MRMediaRemoteGetNowPlayingInfoBlock)(NSDictionary *info); +typedef void (^MRMediaRemoteGetNowPlayingClientBlock)(id clientObj); +typedef void (^MRMediaRemoteGetNowPlayingApplicationIsPlayingBlock)(BOOL playing); + +extern void MRMediaRemoteRegisterForNowPlayingNotifications(dispatch_queue_t queue); +extern void MRMediaRemoteGetNowPlayingClient(dispatch_queue_t queue, MRMediaRemoteGetNowPlayingClientBlock block); +extern void MRMediaRemoteGetNowPlayingClients(dispatch_queue_t queue, MRMediaRemoteGetNowPlayingClientBlock block); +extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t queue, MRMediaRemoteGetNowPlayingInfoBlock block); +extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, MRMediaRemoteGetNowPlayingApplicationIsPlayingBlock block); + +extern NSString *MRNowPlayingClientGetBundleIdentifier(id clientObj); +extern NSString *MRNowPlayingClientGetParentAppBundleIdentifier(id clientObj); + +extern NSString *kMRMediaRemoteNowPlayingApplicationIsPlayingDidChangeNotification; +extern NSString *kMRMediaRemoteNowPlayingApplicationClientStateDidChange; +extern NSString *kMRNowPlayingPlaybackQueueChangedNotification; +extern NSString *kMRPlaybackQueueContentItemsChangedNotification; +extern NSString *kMRMediaRemoteNowPlayingApplicationDidChangeNotification; + +extern NSString *kMRMediaRemoteNowPlayingInfoAlbum; +extern NSString *kMRMediaRemoteNowPlayingInfoArtist; +extern NSString *kMRMediaRemoteNowPlayingInfoTitle; +extern NSString *kMRMediaRemoteNowPlayingInfoArtworkData; + +typedef enum { + kMRPlay = 0, + kMRPause = 1, + kMRTogglePlayPause = 2, + kMRNextTrack = 4, + kMRPreviousTrack = 5, +} MRCommand; + +extern Boolean MRMediaRemoteSendCommand(MRCommand command, id userInfo); diff --git a/Underbar/NSTouchBar.h b/Underbar/Private/NSTouchBar.h similarity index 85% rename from Underbar/NSTouchBar.h rename to Underbar/Private/NSTouchBar.h index 55c77de..a69dab6 100644 --- a/Underbar/NSTouchBar.h +++ b/Underbar/Private/NSTouchBar.h @@ -6,6 +6,8 @@ // Copyright © 2019 Shadowfacts. All rights reserved. // +// Copied from https://github.com/pigigaldi/Pock/blob/852679a4882d0e9c5b3d8473eb9b9fbf7bef837f/Pock/Private/NSTouchBar.h + #import extern void DFRElementSetControlStripPresenceForIdentifier(_Nonnull NSTouchBarItemIdentifier, BOOL); diff --git a/Underbar/Underbar-Bridging-Header.h b/Underbar/Underbar-Bridging-Header.h index f1de2b6..e4ed804 100644 --- a/Underbar/Underbar-Bridging-Header.h +++ b/Underbar/Underbar-Bridging-Header.h @@ -7,3 +7,4 @@ // #import "NSTouchBar.h" +#import "MRMediaRemote.h" diff --git a/Underbar/Widgets/NowPlayingWidget.swift b/Underbar/Widgets/NowPlayingWidget.swift deleted file mode 100644 index 2857391..0000000 --- a/Underbar/Widgets/NowPlayingWidget.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// NowPlayingWidget.swift -// Underbar -// -// Created by Shadowfacts on 7/13/19. -// Copyright © 2019 Shadowfacts. All rights reserved. -// - -import Cocoa - -class NowPlayingWidget: TouchBarWidget { - - override init() { - super.init() - item = NSButtonTouchBarItem(identifier: .nowPlaying, title: "Test", target: nil, action: nil) - } - -} diff --git a/Underbar/Widgets/TouchBarWidget.swift b/Underbar/Widgets/TouchBarWidget.swift deleted file mode 100644 index 0b2ebd8..0000000 --- a/Underbar/Widgets/TouchBarWidget.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// TouchBarWidget.swift -// Underbar -// -// Created by Shadowfacts on 7/13/19. -// Copyright © 2019 Shadowfacts. All rights reserved. -// - -import Cocoa - -class TouchBarWidget: NSObject { - - var item: NSTouchBarItem! - - override init() { - super.init() - } - - convenience init(item: NSTouchBarItem) { - self.init() - self.item = item - } - -}