Compare commits

..
19 Commits
Author SHA1 Message Date
Anton Palgunov 95271dba0e HapticFeedback: fix broken haptic for Release builds + refactori… (#198)
HapticFeedback: fix broken haptic for Release builds + refactoring
2019-07-28 00:17:18 +01:00
bobrosoft 67aaa2abf4 CurrencyBarItem: fix text vertical alignment 2019-07-27 18:10:48 +02:00
bobrosoft 58e4160649 HapticFeedback: fix broken haptic for Release builds + refactoring 2019-07-27 13:05:02 +02:00
toxblh 77846d0436 v0.22 2019-07-26 13:37:49 +01:00
Anton Palgunov bc66b375d3 Merge pull request #195 from bobrosoft/master
README: add "yandexWeather" to the list of "Native Plugins" on top
2019-07-23 16:31:06 +01:00
bobrosoft ce8004641d README: add "yandexWeather" to the list of "Native Plugins" on top 2019-07-23 18:54:07 +04:00
Anton Palgunov 155e48a693 Add new "yandexWeather" widget (#194)
Add new "yandexWeather" widget
2019-07-23 14:41:17 +01:00
bobrosoft 76a7d59fe5 README: add "experimental" flag to yandexWeather widget 2019-07-23 17:11:13 +04:00
bobrosoft 6a85bea5b5 Add new "yandexWeather" widget 2019-07-23 17:07:47 +04:00
Anton Palgunov 000b825ec9 Merge pull request #193 from bobrosoft:master
MusicBarItem: fix for latest macOS + add "disableMarquee" param
2019-07-23 13:36:28 +01:00
bobrosoft 3432e24a55 MusicBarItem: add support for "disableMarquee" param 2019-07-18 18:29:08 +04:00
bobrosoft 705d0a64b5 MusicBarItem: fix for latest macOS 2019-07-17 17:42:03 +04:00
Anton Palgunov 9f0944b06b HapticFeedback: fix recovery from the long sleep (#192)
HapticFeedback: fix recovery from the long sleep
2019-07-17 09:27:26 +01:00
bobrosoft efc52293a8 HapticFeedback: fix recovery from the long sleep (minor comment changes) 2019-07-17 12:21:51 +04:00
bobrosoft 7b853b5d47 HapticFeedback: fix recovery from the long sleep 2019-07-17 12:17:35 +04:00
Anton Palgunov 439246e85b CustomButtonTouchBarItem: add better haptic feedback right when… (#188)
CustomButtonTouchBarItem: add better haptic feedback right when button been touched
2019-07-15 12:57:50 +01:00
bobrosoft c254ee430d CustomButtonTouchBarItem: add better haptic feedback right when button been touched 2019-07-12 17:39:10 +04:00
toxblh cfcda6e46f upd CFBundleVersion 2019-07-11 17:51:13 +01:00
toxblh 144ff9cf79 v0.21.1 2019-07-11 17:49:15 +01:00
11 changed files with 302 additions and 76 deletions
+4
View File
@@ -18,6 +18,7 @@
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */; };
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 60173D3D20C0031B002C305F /* LaunchAtLoginController.m */; };
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
@@ -91,6 +92,7 @@
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = "<group>"; };
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = "<group>"; };
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YandexWeatherBarItem.swift; sourceTree = "<group>"; };
60173D3C20C0031B002C305F /* LaunchAtLoginController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = "<group>"; };
60173D3D20C0031B002C305F /* LaunchAtLoginController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = "<group>"; };
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
@@ -294,6 +296,7 @@
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */,
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
);
@@ -458,6 +461,7 @@
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */,
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */,
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */,
+17 -8
View File
@@ -11,9 +11,9 @@ import Cocoa
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
var tapClosure: (() -> Void)?
var longTapClosure: (() -> Void)?
private var button: NSButton!
private var singleClick: NSClickGestureRecognizer!
private var singleClick: HapticClickGestureRecognizer!
private var longClick: NSPressGestureRecognizer!
init(identifier: NSTouchBarItem.Identifier, title: String) {
@@ -26,7 +26,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
longClick.allowedTouchTypes = .direct
longClick.delegate = self
singleClick = NSClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
singleClick = HapticClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
singleClick.allowedTouchTypes = .direct
singleClick.delegate = self
@@ -104,10 +104,8 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
}
@objc func handleGestureSingle(gr: NSClickGestureRecognizer) {
let hf: HapticFeedback = HapticFeedback()
switch gr.state {
case .ended:
hf.tap(strong: 2)
tapClosure?()
break
default:
@@ -116,14 +114,13 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
}
@objc func handleGestureLong(gr: NSPressGestureRecognizer) {
let hf: HapticFeedback = HapticFeedback()
switch gr.state {
case .began:
if let closure = self.longTapClosure {
hf.tap(strong: 2)
HapticFeedback.shared.tap(strong: 2)
closure()
} else if let closure = self.tapClosure {
hf.tap(strong: 6)
HapticFeedback.shared.tap(strong: 6)
closure()
print("long click")
}
@@ -172,6 +169,18 @@ class CustomButtonCell: NSButtonCell {
}
}
class HapticClickGestureRecognizer: NSClickGestureRecognizer {
override func touchesBegan(with event: NSEvent) {
HapticFeedback.shared.tap(strong: 2)
super.touchesBegan(with: event)
}
override func touchesEnded(with event: NSEvent) {
HapticFeedback.shared.tap(strong: 1)
super.touchesEnded(with: event)
}
}
extension String {
var defaultTouchbarAttributedString: NSAttributedString {
let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1])
+30 -10
View File
@@ -9,24 +9,22 @@
import IOKit
class HapticFeedback {
private var actuatorRef: CFTypeRef?
static let shared = HapticFeedback()
// Here we have list of possible IDs for Haptic Generator Device. They are not constant
// To find deviceID, you will need IORegistryExplorer app from Additional Tools for Xcode dmg
// which you can download from https://developer.apple.com/download/more/?=Additional%20Tools
// Open IORegistryExplorer app, search for AppleMultitouchDevice and get "Multitouch ID"
// There should be programmatic way to get it but I can't find, no docs for macOS :(
private var possibleDeviceIDs: [UInt64] = [
private let possibleDeviceIDs: [UInt64] = [
0x200_0000_0100_0000, // MacBook Pro 2016/2017
0x300000080500000 // MacBook Pro 2019 (possibly 2018 as well)
]
private var correctDeviceID: UInt64?
private var actuatorRef: CFTypeRef?
init() {
// Let's find and init Haptic device
possibleDeviceIDs.forEach {(deviceID) in
guard actuatorRef == nil else {return}
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
}
recreateDevice()
}
// Don't know how to do strong is enum one of
@@ -41,8 +39,8 @@ class HapticFeedback {
// you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p`
func tap(strong: Int32) {
guard actuatorRef != nil else {
print("guard actuatorRef == nil (can't find proper haptic device?)")
guard correctDeviceID != nil, actuatorRef != nil else {
print("guard actuatorRef == nil (no haptic device found?)")
return
}
@@ -51,10 +49,11 @@ class HapticFeedback {
result = MTActuatorOpen(actuatorRef!)
guard result == kIOReturnSuccess else {
print("guard MTActuatorOpen")
recreateDevice()
return
}
result = MTActuatorActuate(actuatorRef!, strong, 0, 0.0, 0.0)
result = MTActuatorActuate(actuatorRef!, strong, 0, 0, 0)
guard result == kIOReturnSuccess else {
print("guard MTActuatorActuate")
return
@@ -66,4 +65,25 @@ class HapticFeedback {
return
}
}
private func recreateDevice() {
if let actuatorRef = actuatorRef {
MTActuatorClose(actuatorRef)
self.actuatorRef = nil // just in case %)
}
if let correctDeviceID = correctDeviceID {
actuatorRef = MTActuatorCreateFromDeviceID(correctDeviceID).takeRetainedValue()
} else {
// Let's find our Haptic device
possibleDeviceIDs.forEach {(deviceID) in
guard correctDeviceID == nil else {return}
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
if actuatorRef != nil {
correctDeviceID = deviceID
}
}
}
}
}
+2 -2
View File
@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.21</string>
<string>0.22</string>
<key>CFBundleVersion</key>
<string>252</string>
<string>270</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
+28 -5
View File
@@ -179,6 +179,20 @@ class SupportedTypesHolder {
parameters: [:]
)
},
"yandexWeather": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval }
let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
let action = try ActionType(from: decoder)
let longAction = try LongActionType(from: decoder)
return (
item: .yandexWeather(interval: interval ?? 1800.00),
action,
longAction,
parameters: [:]
)
},
"currency": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case full }
@@ -274,11 +288,12 @@ class SupportedTypesHolder {
) },
"music": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval }
enum CodingKeys: String, CodingKey { case refreshInterval; case disableMarquee }
let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
let disableMarquee = try container.decodeIfPresent(Bool.self, forKey: .disableMarquee)
return (
item: .music(interval: interval ?? 1800.00),
item: .music(interval: interval ?? 5.0, disableMarquee: disableMarquee ?? false),
action: .none,
longAction: .none,
parameters: [:]
@@ -334,9 +349,10 @@ enum ItemType: Decodable {
case volume()
case brightness(refreshInterval: Double)
case weather(interval: Double, units: String, api_key: String, icon_type: String)
case yandexWeather(interval: Double)
case currency(interval: Double, from: String, to: String, full: Bool)
case inputsource()
case music(interval: Double)
case music(interval: Double, disableMarquee: Bool)
case groupBar(items: [BarItemDefinition])
case nightShift()
case dnd()
@@ -365,6 +381,7 @@ enum ItemType: Decodable {
case restTime
case flip
case autoResize
case disableMarquee
}
enum ItemTypeRaw: String, Decodable {
@@ -376,6 +393,7 @@ enum ItemType: Decodable {
case volume
case brightness
case weather
case yandexWeather
case currency
case inputsource
case music
@@ -425,6 +443,10 @@ enum ItemType: Decodable {
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case .yandexWeather:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .yandexWeather(interval: interval)
case .currency:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
@@ -437,8 +459,9 @@ enum ItemType: Decodable {
self = .inputsource()
case .music:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .music(interval: interval)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 5.0
let disableMarquee = try container.decodeIfPresent(Bool.self, forKey: .disableMarquee) ?? false
self = .music(interval: interval, disableMarquee: disableMarquee)
case .groupBar:
let items = try container.decode([BarItemDefinition].self, forKey: .items)
+6 -2
View File
@@ -35,6 +35,8 @@ extension ItemType {
return "com.toxblh.mtmr.brightness"
case .weather(interval: _, units: _, api_key: _, icon_type: _):
return "com.toxblh.mtmr.weather"
case .yandexWeather(interval: _):
return "com.toxblh.mtmr.yandexWeather"
case .currency(interval: _, from: _, to: _, full: _):
return "com.toxblh.mtmr.currency"
case .inputsource():
@@ -269,12 +271,14 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
}
case let .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type):
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case let .yandexWeather(interval: interval):
barItem = YandexWeatherBarItem(identifier: identifier, interval: interval)
case let .currency(interval: interval, from: from, to: to, full: full):
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
case .inputsource():
barItem = InputSourceBarItem(identifier: identifier)
case let .music(interval: interval):
barItem = MusicBarItem(identifier: identifier, interval: interval)
case let .music(interval: interval, disableMarquee: disableMarquee):
barItem = MusicBarItem(identifier: identifier, interval: interval, disableMarquee: disableMarquee)
case let .groupBar(items: items):
barItem = GroupBarItem(identifier: identifier, items: items)
case .nightShift():
+4 -6
View File
@@ -10,8 +10,6 @@ import Cocoa
class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource {
private var scrubber: NSScrubber!
private let hf: HapticFeedback = HapticFeedback()
private var timer: Timer!
private var ticks: Int = 0
private let minTicks: Int = 5
@@ -148,12 +146,12 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
ticks += 1
if ticks == minTicks {
hf.tap(strong: 2)
HapticFeedback.shared.tap(strong: 2)
}
if ticks > maxTicks {
stopTimer()
hf.tap(strong: 6)
HapticFeedback.shared.tap(strong: 6)
}
}
@@ -184,7 +182,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: ""))
} else {
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
hf.tap(strong: 6)
HapticFeedback.shared.tap(strong: 6)
}
updateRunningApplication()
@@ -203,7 +201,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
}
}
} else {
hf.tap(strong: 6)
HapticFeedback.shared.tap(strong: 6)
if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) {
persistentAppIdentifiers.remove(at: index)
} else {
+1 -1
View File
@@ -123,7 +123,7 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
}
let regularFont = attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont])
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont, .baselineOffset: 1])
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
attributedTitle = newTitle
}
+50 -39
View File
@@ -10,21 +10,32 @@ import Cocoa
import ScriptingBridge
class MusicBarItem: CustomButtonTouchBarItem {
private enum Player: String {
case iTunes = "com.apple.iTunes"
case Spotify = "com.spotify.client"
case VOX = "com.coppertino.Vox"
case Chrome = "com.google.Chrome"
case Safari = "com.apple.Safari"
}
private let playerBundleIdentifiers = [
Player.iTunes,
Player.Spotify,
Player.VOX,
Player.Chrome,
Player.Safari,
]
private let interval: TimeInterval
private let disableMarquee: Bool
private var songTitle: String?
private var timer: Timer?
let buttonSize = NSSize(width: 21, height: 21)
private let iconSize = NSSize(width: 21, height: 21)
let playerBundleIdentifiers = [
"com.apple.iTunes",
"com.spotify.client",
"com.coppertino.Vox",
"com.google.Chrome",
"com.apple.Safari",
]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, disableMarquee: Bool) {
self.interval = interval
self.disableMarquee = disableMarquee
super.init(identifier: identifier, title: "⏳")
isBordered = false
@@ -50,21 +61,21 @@ class MusicBarItem: CustomButtonTouchBarItem {
@objc func playPause() {
for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
if musicPlayer.isRunning {
if musicPlayer.className == "SpotifyApplication" {
if ident == .Spotify {
let mp = (musicPlayer as SpotifyApplication)
mp.playpause!()
return
} else if musicPlayer.className == "ITunesApplication" {
} else if ident == .iTunes {
let mp = (musicPlayer as iTunesApplication)
mp.playpause!()
return
} else if musicPlayer.className == "VOXApplication" {
} else if ident == .VOX {
let mp = (musicPlayer as VoxApplication)
mp.playpause!()
return
} else if musicPlayer.className == "SafariApplication" {
} else if ident == .Safari {
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
@@ -84,7 +95,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
}
}
}
// else if (musicPlayer.className == "GoogleChromeApplication") {
// else if (ident == .Chrome) {
// let chromeApplication = musicPlayer as GoogleChromeApplication
// let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
// for window in chromeWindows! {
@@ -111,24 +122,24 @@ class MusicBarItem: CustomButtonTouchBarItem {
@objc func nextTrack() {
for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
if musicPlayer.isRunning {
if musicPlayer.className == "SpotifyApplication" {
if ident == .Spotify {
let mp = (musicPlayer as SpotifyApplication)
mp.nextTrack!()
updatePlayer()
return
} else if musicPlayer.className == "ITunesApplication" {
} else if ident == .iTunes {
let mp = (musicPlayer as iTunesApplication)
mp.nextTrack!()
updatePlayer()
return
} else if musicPlayer.className == "VOXApplication" {
} else if ident == .VOX {
let mp = (musicPlayer as VoxApplication)
mp.next!()
updatePlayer()
return
} else if musicPlayer.className == "SafariApplication" {
} else if ident == .Safari {
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
@@ -170,16 +181,16 @@ class MusicBarItem: CustomButtonTouchBarItem {
var titleUpdated = false
for var ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
if musicPlayer.isRunning {
var tempTitle = ""
if musicPlayer.className == "SpotifyApplication" {
if ident == .Spotify {
tempTitle = (musicPlayer as SpotifyApplication).title
} else if musicPlayer.className == "ITunesApplication" {
} else if ident == .iTunes {
tempTitle = (musicPlayer as iTunesApplication).title
} else if musicPlayer.className == "VOXApplication" {
} else if ident == .VOX {
tempTitle = (musicPlayer as VoxApplication).title
} else if musicPlayer.className == "SafariApplication" {
} else if ident == .Safari {
let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! {
@@ -199,10 +210,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
}
}
}
if tempTitle == "" {
ident = ""
}
} else if musicPlayer.className == "GoogleChromeApplication" {
} else if ident == .Chrome {
let chromeApplication = musicPlayer as GoogleChromeApplication
let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
for window in chromeWindows! {
@@ -222,9 +230,6 @@ class MusicBarItem: CustomButtonTouchBarItem {
}
}
}
if tempTitle == "" {
ident = ""
}
}
if tempTitle == self.songTitle {
@@ -234,16 +239,22 @@ class MusicBarItem: CustomButtonTouchBarItem {
}
if let songTitle = self.songTitle?.ifNotEmpty {
self.title = " " + songTitle + " "
titleUpdated = true
self.timer?.invalidate()
self.timer = nil
self.timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(self.marquee), userInfo: nil, repeats: true)
if (disableMarquee) {
self.title = " " + songTitle
} else {
self.title = " " + songTitle + " "
self.timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(self.marquee), userInfo: nil, repeats: true)
}
titleUpdated = true
}
if let ident = ident.ifNotEmpty,
let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident) {
if let _ = tempTitle.ifNotEmpty,
let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident.rawValue) {
let image = NSWorkspace.shared.icon(forFile: appPath)
image.size = self.buttonSize
image.size = self.iconSize
self.image = image
iconUpdated = true
}
+145
View File
@@ -0,0 +1,145 @@
//
// YandexWeatherBarItem.swift
// MTMR
//
// Created by bobrosoft on 22/07/2019.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import CoreLocation
class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private let activity: NSBackgroundActivityScheduler
private let unitsStr = "°C"
private let iconsSource = ["Ясно": "☀️", "Малооблачно": "🌤", "Облачно с прояснениями": "⛅️", "Пасмурно": "☁️", "Небольшой дождь": "🌦", "Дождь": "🌧", "Ливень": "⛈", "Гроза": "🌩", "Небольшой снег": "❄️", "Снег": "🌨", "Туман": "🌫"]
private var location: CLLocation!
private var prevLocation: CLLocation!
private var manager: CLLocationManager!
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval
super.init(identifier: identifier, title: "⏳")
let status = CLLocationManager.authorizationStatus()
if status == .restricted || status == .denied {
print("User permission not given")
return
}
if !CLLocationManager.locationServicesEnabled() {
print("Location services not enabled")
return
}
activity.repeats = true
activity.qualityOfService = .utility
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
self.updateWeather()
completion(NSBackgroundActivityScheduler.Result.finished)
}
updateWeather()
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
manager.startUpdatingLocation()
tapClosure = tapClosure ?? defaultTapAction
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateWeather() {
var urlRequest = URLRequest(url: URL(string: getWeatherUrl())!)
urlRequest.addValue("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", forHTTPHeaderField: "user-agent") // important for the right format
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
guard error == nil, let response = data?.utf8string else {
return
}
// print(response)
var matches: [[String]]
var temperature: String?
matches = response.matchingStrings(regex: "fact__temp.*?temp__value.*?>(.*?)<")
temperature = matches.first?.item(at: 1)
var icon: String?
matches = response.matchingStrings(regex: "link__condition.*?>(.*?)<")
icon = matches.first?.item(at: 1)
if let _ = icon, let test = self.iconsSource[icon!] {
icon = test
}
if temperature != nil {
DispatchQueue.main.async {
self.setWeather(text: "\(icon ?? "?") \(temperature!)\(self.unitsStr)")
}
}
}
task.resume()
}
func getWeatherUrl() -> String {
if location != nil {
return "https://yandex.ru/pogoda/?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)"
} else {
return "https://yandex.ru/pogoda/" // Yandex will try to determine your location by default
}
}
func setWeather(text: String) {
title = text
}
func defaultTapAction() {
print(getWeatherUrl())
if let url = URL(string: getWeatherUrl()) {
NSWorkspace.shared.open(url)
}
}
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
location = lastLocation
if prevLocation == nil {
updateWeather()
}
prevLocation = lastLocation
}
func locationManager(_: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func locationManager(_: CLLocationManager, didChangeAuthorization _: CLAuthorizationStatus) {
updateWeather()
}
}
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.range(at: $0).location != NSNotFound
? nsString.substring(with: result.range(at: $0))
: ""
}
}
}
}
extension Array {
func item(at index: Int) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
+15 -3
View File
@@ -77,6 +77,7 @@ The pre-installed configuration contains less or more than you'll probably want,
- battery
- currency
- weather
- yandexWeather
- inputsource
- music (tap for pause, longTap for next)
- dock (half-long click to open app, full-long click to kill app)
@@ -188,6 +189,16 @@ To close a group, use the button:
"api_key": "" // you can get the key on openweather
```
#### `yandexWeather` (experimental)
> Provider: https://yandex.ru/pogoda. One click to open up weather forecast in your browser. \
> Note: you need to allow using "Location Services" in your Mac OS "Security & Privacy" settings for MTMR
```js
"type": "yandexWeather",
"refreshInterval": 600 // in seconds
```
#### `currency`
> Provider: https://coinbase.com
@@ -207,9 +218,10 @@ To close a group, use the button:
{
"type": "music",
"align": "center",
"width": 80,
"bordered": false,
"refreshInterval": 2, // in seconds
"width": 80, // Optional
"bordered": false, // Optional
"refreshInterval": 2, // in seconds. Optional. Default 5 seconds
"disableMarquee": true // to disable marquee effect. Optional. Default false
},
```