mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-01-12 09:58:38 +00:00
merged master
This commit is contained in:
commit
f78f82893a
@ -18,6 +18,8 @@
|
|||||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
|
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
|
||||||
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
|
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
|
||||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
|
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
|
||||||
|
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */; };
|
||||||
|
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */; };
|
||||||
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
|
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
|
||||||
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */; };
|
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */; };
|
||||||
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05600D22083E9BB00EB218D /* CustomSlider.swift */; };
|
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05600D22083E9BB00EB218D /* CustomSlider.swift */; };
|
||||||
@ -67,6 +69,8 @@
|
|||||||
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScrubberTouchBarItem.swift; sourceTree = "<group>"; };
|
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScrubberTouchBarItem.swift; sourceTree = "<group>"; };
|
||||||
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeprecatedCarbonAPI.h; sourceTree = "<group>"; };
|
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeprecatedCarbonAPI.h; sourceTree = "<group>"; };
|
||||||
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; };
|
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; };
|
||||||
|
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherBarItem.swift; sourceTree = "<group>"; };
|
||||||
|
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyBarItem.swift; sourceTree = "<group>"; };
|
||||||
B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; };
|
B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; };
|
||||||
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryBarItem.swift; sourceTree = "<group>"; };
|
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryBarItem.swift; sourceTree = "<group>"; };
|
||||||
B05600D22083E9BB00EB218D /* CustomSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSlider.swift; sourceTree = "<group>"; };
|
B05600D22083E9BB00EB218D /* CustomSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSlider.swift; sourceTree = "<group>"; };
|
||||||
@ -175,6 +179,8 @@
|
|||||||
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
|
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
|
||||||
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
|
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
|
||||||
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
|
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
|
||||||
|
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
|
||||||
|
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
|
||||||
);
|
);
|
||||||
path = MTMR;
|
path = MTMR;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -343,11 +349,13 @@
|
|||||||
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
|
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
|
||||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
||||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
||||||
|
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
|
||||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
||||||
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
||||||
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
|
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
|
||||||
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
|
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
|
||||||
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
|
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
|
||||||
|
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */,
|
||||||
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
|
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
|
||||||
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */,
|
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */,
|
||||||
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */,
|
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */,
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>BuildSystemType</key>
|
||||||
|
<string>Latest</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -39,10 +39,36 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
TouchBarController.shared.createAndUpdatePreset()
|
TouchBarController.shared.createAndUpdatePreset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func openPreset(_ sender: Any?) {
|
||||||
|
let dialog = NSOpenPanel();
|
||||||
|
|
||||||
|
dialog.title = "Choose a items.json file"
|
||||||
|
dialog.showsResizeIndicator = true
|
||||||
|
dialog.showsHiddenFiles = true
|
||||||
|
dialog.canChooseDirectories = false
|
||||||
|
dialog.canCreateDirectories = false
|
||||||
|
dialog.allowsMultipleSelection = false
|
||||||
|
dialog.allowedFileTypes = ["json"]
|
||||||
|
dialog.directoryURL = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR"), isDirectory: true)
|
||||||
|
|
||||||
|
if (dialog.runModal() == NSApplication.ModalResponse.OK) {
|
||||||
|
let result = dialog.url
|
||||||
|
|
||||||
|
if (result != nil) {
|
||||||
|
let path = result!.path
|
||||||
|
let jsonData = path.fileData
|
||||||
|
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [:])]
|
||||||
|
|
||||||
|
TouchBarController.shared.createAndUpdatePreset(jsonItems: jsonItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createMenu() {
|
func createMenu() {
|
||||||
let menu = NSMenu()
|
let menu = NSMenu()
|
||||||
menu.addItem(withTitle: "Preferences", action: #selector(openPrefereces(_:)), keyEquivalent: ",")
|
menu.addItem(withTitle: "Preferences", action: #selector(openPrefereces(_:)), keyEquivalent: ",")
|
||||||
menu.addItem(withTitle: "Reload Preset", action: #selector(updatePreset(_:)), keyEquivalent: "r")
|
menu.addItem(withTitle: "Reload Preset", action: #selector(updatePreset(_:)), keyEquivalent: "r")
|
||||||
|
menu.addItem(withTitle: "Open Preset", action: #selector(openPreset(_:)), keyEquivalent: "O")
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
||||||
statusItem.menu = menu
|
statusItem.menu = menu
|
||||||
|
|||||||
@ -35,8 +35,10 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
scrubber.mode = .free // .fixed
|
scrubber.mode = .free // .fixed
|
||||||
let layout = NSScrubberFlowLayout();
|
let layout = NSScrubberFlowLayout();
|
||||||
layout.itemSize = NSSize(width: 44, height: 30)
|
layout.itemSize = NSSize(width: 44, height: 30)
|
||||||
|
layout.itemSpacing = 2
|
||||||
scrubber.scrubberLayout = layout
|
scrubber.scrubberLayout = layout
|
||||||
scrubber.selectionBackgroundStyle = .roundedBackground
|
scrubber.selectionBackgroundStyle = .roundedBackground
|
||||||
|
scrubber.showsAdditionalContentIndicators = true
|
||||||
|
|
||||||
view = scrubber
|
view = scrubber
|
||||||
|
|
||||||
@ -90,20 +92,19 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func didFinishInteracting(with scrubber: NSScrubber) {
|
public func didFinishInteracting(with scrubber: NSScrubber) {
|
||||||
runningApplications[scrubber.selectedIndex].activate(options: [ .activateIgnoringOtherApps ])
|
// runningApplications[scrubber.selectedIndex].activate(options: [ .activateIgnoringOtherApps ])
|
||||||
|
|
||||||
|
let bundleIdentifier = runningApplications[scrubber.selectedIndex].bundleIdentifier
|
||||||
|
if bundleIdentifier!.contains("file://") {
|
||||||
|
NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: ""))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
|
||||||
|
}
|
||||||
|
|
||||||
// NB: if you can't open app which on another space, try to check mark
|
// NB: if you can't open app which on another space, try to check mark
|
||||||
// "When switching to an application, switch to a Space with open windows for the application"
|
// "When switching to an application, switch to a Space with open windows for the application"
|
||||||
// in Mission control settings
|
// in Mission control settings
|
||||||
|
|
||||||
// TODO: deminiaturize app
|
|
||||||
// if let info = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[ String : Any]] {
|
|
||||||
// for dict in info {
|
|
||||||
// if dict["kCGWindowOwnerName"] as! String == runningApplications[scrubber.selectedIndex].localizedName {
|
|
||||||
// print(dict["kCGWindowNumber"], dict["kCGWindowOwnerName"])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
|
|
||||||
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->()) {
|
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->()) {
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
super.init(identifier: identifier, title: "compile", onTap: onTap)
|
super.init(identifier: identifier, title: "⏳", onTap: onTap)
|
||||||
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
|
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
|
||||||
guard let script = source.appleScript else {
|
guard let script = source.appleScript else {
|
||||||
button.title = "no script"
|
button.title = "no script"
|
||||||
@ -18,9 +18,9 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
var error: NSDictionary?
|
var error: NSDictionary?
|
||||||
guard script.compileAndReturnError(&error) else {
|
guard script.compileAndReturnError(&error) else {
|
||||||
print(error?.description ?? "unknown error")
|
// print(error?.description ?? "unknown error")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.button.title = "compile error"
|
self.button.title = "error"
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func refreshAndSchedule() {
|
func refreshAndSchedule() {
|
||||||
print("refresh happened")
|
// print("refresh happened")
|
||||||
let scriptResult = self.execute()
|
let scriptResult = self.execute()
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.button.title = scriptResult
|
self.button.title = scriptResult
|
||||||
|
|||||||
115
MTMR/CurrencyBarItem.swift
Normal file
115
MTMR/CurrencyBarItem.swift
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
//
|
||||||
|
// CurrencyBarItem.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Daniel Apatin on 18.04.2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||||
|
private var timer: Timer!
|
||||||
|
private var interval: TimeInterval!
|
||||||
|
private var prefix: String
|
||||||
|
private var from: String
|
||||||
|
private var to: String
|
||||||
|
private var oldValue: Float32!
|
||||||
|
|
||||||
|
private let currencies = [
|
||||||
|
"USD": "$",
|
||||||
|
"EUR": "€",
|
||||||
|
"RUB": "₽",
|
||||||
|
"JPY": "¥",
|
||||||
|
"GBP": "₤",
|
||||||
|
"CAD": "$",
|
||||||
|
"KRW": "₩",
|
||||||
|
"CNY": "¥",
|
||||||
|
"AUD": "$",
|
||||||
|
"BRL": "R$",
|
||||||
|
"IDR": "Rp",
|
||||||
|
"MXN": "$",
|
||||||
|
"SGD": "$",
|
||||||
|
"CHF": "Fr."
|
||||||
|
]
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, onTap: @escaping () -> ()) {
|
||||||
|
self.interval = interval
|
||||||
|
self.from = from
|
||||||
|
self.to = to
|
||||||
|
|
||||||
|
if let prefix = currencies[from] {
|
||||||
|
self.prefix = prefix
|
||||||
|
} else {
|
||||||
|
self.prefix = from
|
||||||
|
}
|
||||||
|
|
||||||
|
super.init(identifier: identifier, title: "⏳", onTap: onTap)
|
||||||
|
|
||||||
|
button.bezelColor = .clear
|
||||||
|
self.view = button
|
||||||
|
|
||||||
|
timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(updateCurrency), userInfo: nil, repeats: true)
|
||||||
|
|
||||||
|
updateCurrency()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func updateCurrency() {
|
||||||
|
let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!)
|
||||||
|
|
||||||
|
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
|
||||||
|
if error == nil {
|
||||||
|
do {
|
||||||
|
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
|
||||||
|
|
||||||
|
var value: Float32!
|
||||||
|
|
||||||
|
if let data_array = json["data"] as? [String : AnyObject] {
|
||||||
|
if let rates = data_array["rates"] as? [String : AnyObject] {
|
||||||
|
if let item = rates["\(self.to)"] as? String {
|
||||||
|
value = Float32(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if value != nil {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.setCurrency(value: value!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch let jsonError {
|
||||||
|
print(jsonError.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCurrency(value: Float32) {
|
||||||
|
var color = NSColor.white
|
||||||
|
|
||||||
|
if let oldValue = self.oldValue {
|
||||||
|
if oldValue < value {
|
||||||
|
color = NSColor(red: 95.0/255.0, green: 185.0/255.0, blue: 50.0/255.0, alpha: 1.0)
|
||||||
|
} else if oldValue > value {
|
||||||
|
color = NSColor(red: 185.0/255.0, green: 95.0/255.0, blue: 50.0/255.0, alpha: 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.oldValue = value
|
||||||
|
|
||||||
|
button.title = String(format: "%@%.2f", self.prefix, value)
|
||||||
|
|
||||||
|
let textRange = NSRange(location: 0, length: button.title.count)
|
||||||
|
let newTitle = NSMutableAttributedString(string: button.title)
|
||||||
|
newTitle.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: textRange)
|
||||||
|
newTitle.addAttribute(NSAttributedStringKey.font, value: button.font!, range: textRange)
|
||||||
|
newTitle.setAlignment(.center, range: textRange)
|
||||||
|
|
||||||
|
button.attributedTitle = newTitle
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,6 +26,8 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
|
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
|
||||||
|
<key>NSLocationUsageDescription</key>
|
||||||
|
<string>...</string>
|
||||||
<key>NSMainStoryboardFile</key>
|
<key>NSMainStoryboardFile</key>
|
||||||
<string>Main</string>
|
<string>Main</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
|
|||||||
@ -60,6 +60,10 @@ class SupportedTypesHolder {
|
|||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
"mute": { _ in
|
||||||
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarAudioOutputMuteTemplate)!)
|
||||||
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), parameters: [.image: imageParameter])
|
||||||
|
},
|
||||||
"previous": { _ in
|
"previous": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), parameters: [.image: imageParameter])
|
||||||
@ -73,12 +77,23 @@ class SupportedTypesHolder {
|
|||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"weather": { decoder in
|
"weather": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval }
|
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
let scriptPath = Bundle.main.path(forResource: "Weather", ofType: "scpt")!
|
let units = try container.decodeIfPresent(String.self, forKey: .units)
|
||||||
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key)
|
||||||
return (item: item, action: .none, parameters: [:])
|
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type)
|
||||||
|
let action = try ActionType(from: decoder)
|
||||||
|
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, parameters: [:])
|
||||||
|
},
|
||||||
|
"currency": { decoder in
|
||||||
|
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to }
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
|
let from = try container.decodeIfPresent(String.self, forKey: .from)
|
||||||
|
let to = try container.decodeIfPresent(String.self, forKey: .to)
|
||||||
|
let action = try ActionType(from: decoder)
|
||||||
|
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, parameters: [:])
|
||||||
},
|
},
|
||||||
"dock": { decoder in
|
"dock": { decoder in
|
||||||
return (item: .dock(), action: .none, parameters: [:])
|
return (item: .dock(), action: .none, parameters: [:])
|
||||||
@ -108,7 +123,8 @@ class SupportedTypesHolder {
|
|||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
let scriptPath = Bundle.main.path(forResource: "Battery", ofType: "scpt")!
|
let scriptPath = Bundle.main.path(forResource: "Battery", ofType: "scpt")!
|
||||||
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
||||||
return (item: item, action: .none, parameters: [:])
|
let action = try ActionType(from: decoder)
|
||||||
|
return (item: item, action: action, parameters: [:])
|
||||||
},
|
},
|
||||||
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), parameters: [:]) },
|
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), parameters: [:]) },
|
||||||
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), parameters: [:]) },
|
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), parameters: [:]) },
|
||||||
@ -141,14 +157,22 @@ enum ItemType: Decodable {
|
|||||||
case dock()
|
case dock()
|
||||||
case volume()
|
case volume()
|
||||||
case brightness(refreshInterval: Double)
|
case brightness(refreshInterval: Double)
|
||||||
|
case weather(interval: Double, units: String, api_key: String, icon_type: String)
|
||||||
|
case currency(interval: Double, from: String, to: String)
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case type
|
case type
|
||||||
case title
|
case title
|
||||||
case source
|
case source
|
||||||
case refreshInterval
|
case refreshInterval
|
||||||
|
case from
|
||||||
|
case to
|
||||||
|
case units
|
||||||
|
case api_key
|
||||||
|
case icon_type
|
||||||
case formatTemplate
|
case formatTemplate
|
||||||
case image
|
case image
|
||||||
|
case url
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ItemTypeRaw: String, Decodable {
|
enum ItemTypeRaw: String, Decodable {
|
||||||
@ -159,6 +183,8 @@ enum ItemType: Decodable {
|
|||||||
case dock
|
case dock
|
||||||
case volume
|
case volume
|
||||||
case brightness
|
case brightness
|
||||||
|
case weather
|
||||||
|
case currency
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
@ -184,6 +210,17 @@ enum ItemType: Decodable {
|
|||||||
case .brightness:
|
case .brightness:
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
|
||||||
self = .brightness(refreshInterval: interval)
|
self = .brightness(refreshInterval: interval)
|
||||||
|
case .weather:
|
||||||
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||||
|
let units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
|
||||||
|
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 .currency:
|
||||||
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||||
|
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
|
||||||
|
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
|
||||||
|
self = .currency(interval: interval, from: from, to: to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,6 +232,7 @@ enum ActionType: Decodable {
|
|||||||
case appleSctipt(source: SourceProtocol)
|
case appleSctipt(source: SourceProtocol)
|
||||||
case shellScript(executable: String, parameters: [String])
|
case shellScript(executable: String, parameters: [String])
|
||||||
case custom(closure: ()->())
|
case custom(closure: ()->())
|
||||||
|
case openUrl(url: String)
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case action
|
case action
|
||||||
@ -202,6 +240,7 @@ enum ActionType: Decodable {
|
|||||||
case actionAppleScript
|
case actionAppleScript
|
||||||
case executablePath
|
case executablePath
|
||||||
case shellArguments
|
case shellArguments
|
||||||
|
case url
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ActionTypeRaw: String, Decodable {
|
private enum ActionTypeRaw: String, Decodable {
|
||||||
@ -209,6 +248,7 @@ enum ActionType: Decodable {
|
|||||||
case keyPress
|
case keyPress
|
||||||
case appleScript
|
case appleScript
|
||||||
case shellScript
|
case shellScript
|
||||||
|
case openUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
@ -228,6 +268,9 @@ enum ActionType: Decodable {
|
|||||||
let executable = try container.decode(String.self, forKey: .executablePath)
|
let executable = try container.decode(String.self, forKey: .executablePath)
|
||||||
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
||||||
self = .shellScript(executable: executable, parameters: parameters)
|
self = .shellScript(executable: executable, parameters: parameters)
|
||||||
|
case .some(.openUrl):
|
||||||
|
let url = try container.decode(String.self, forKey: .url)
|
||||||
|
self = .openUrl(url: url)
|
||||||
case .none:
|
case .none:
|
||||||
self = .none
|
self = .none
|
||||||
}
|
}
|
||||||
@ -259,6 +302,8 @@ func ==(lhs: ActionType, rhs: ActionType) -> Bool {
|
|||||||
return a == b
|
return a == b
|
||||||
case let (.shellScript(a, b), .shellScript(c, d)):
|
case let (.shellScript(a, b), .shellScript(c, d)):
|
||||||
return a == c && b == d
|
return a == c && b == d
|
||||||
|
case let (.openUrl(a), .openUrl(b)):
|
||||||
|
return a == b
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,6 +58,7 @@ func HIDPostAuxKey(_ key: Int) {
|
|||||||
// hidsystem/ev_keymap.h
|
// hidsystem/ev_keymap.h
|
||||||
let NX_KEYTYPE_SOUND_UP = 0
|
let NX_KEYTYPE_SOUND_UP = 0
|
||||||
let NX_KEYTYPE_SOUND_DOWN = 1
|
let NX_KEYTYPE_SOUND_DOWN = 1
|
||||||
|
let NX_KEYTYPE_MUTE = 7
|
||||||
|
|
||||||
let NX_KEYTYPE_BRIGHTNESS_UP = 2
|
let NX_KEYTYPE_BRIGHTNESS_UP = 2
|
||||||
let NX_KEYTYPE_BRIGHTNESS_DOWN = 3
|
let NX_KEYTYPE_BRIGHTNESS_DOWN = 3
|
||||||
@ -69,3 +70,4 @@ let NX_KEYTYPE_PREVIOUS = 18
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,10 @@ extension ItemType {
|
|||||||
return "com.toxblh.mtmr.volume"
|
return "com.toxblh.mtmr.volume"
|
||||||
case .brightness(refreshInterval: _):
|
case .brightness(refreshInterval: _):
|
||||||
return "com.toxblh.mtmr.brightness"
|
return "com.toxblh.mtmr.brightness"
|
||||||
|
case .weather(interval: _, units: _, api_key: _, icon_type: _):
|
||||||
|
return "com.toxblh.mtmr.weather"
|
||||||
|
case .currency(interval: _, from: _, to: _):
|
||||||
|
return "com.toxblh.mtmr.currency"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,14 +66,18 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
createAndUpdatePreset()
|
createAndUpdatePreset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAndUpdatePreset() {
|
func createAndUpdatePreset(jsonItems: [BarItemDefinition]? = nil) {
|
||||||
|
var jsonItems = jsonItems
|
||||||
self.itemDefinitions = [:]
|
self.itemDefinitions = [:]
|
||||||
self.items = [:]
|
self.items = [:]
|
||||||
self.leftIdentifiers = []
|
self.leftIdentifiers = []
|
||||||
self.centerItems = []
|
self.centerItems = []
|
||||||
self.rightIdentifiers = []
|
self.rightIdentifiers = []
|
||||||
|
|
||||||
loadItemDefinitions()
|
if (jsonItems == nil) {
|
||||||
|
jsonItems = readConfig()
|
||||||
|
}
|
||||||
|
loadItemDefinitions(jsonItems: jsonItems!)
|
||||||
createItems()
|
createItems()
|
||||||
centerItems = self.itemDefinitions.compactMap { (identifier, definition) -> NSTouchBarItem? in
|
centerItems = self.itemDefinitions.compactMap { (identifier, definition) -> NSTouchBarItem? in
|
||||||
return definition.align == .center ? items[identifier] : nil
|
return definition.align == .center ? items[identifier] : nil
|
||||||
@ -80,7 +88,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
self.presentTouchBar()
|
self.presentTouchBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadItemDefinitions() {
|
func readConfig() -> [BarItemDefinition]? {
|
||||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||||
let presetPath = appSupportDirectory.appending("/items.json")
|
let presetPath = appSupportDirectory.appending("/items.json")
|
||||||
if !FileManager.default.fileExists(atPath: presetPath),
|
if !FileManager.default.fileExists(atPath: presetPath),
|
||||||
@ -88,9 +96,13 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
try? FileManager.default.createDirectory(atPath: appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
|
try? FileManager.default.createDirectory(atPath: appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||||
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
|
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
|
||||||
}
|
}
|
||||||
let jsonData = presetPath.fileData
|
|
||||||
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [:])]
|
|
||||||
|
|
||||||
|
let jsonData = presetPath.fileData
|
||||||
|
|
||||||
|
return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [:])]
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
|
||||||
for item in jsonItems {
|
for item in jsonItems {
|
||||||
let identifierString = item.type.identifierBase.appending(UUID().uuidString)
|
let identifierString = item.type.identifierBase.appending(UUID().uuidString)
|
||||||
let identifier = NSTouchBarItem.Identifier(identifierString)
|
let identifier = NSTouchBarItem.Identifier(identifierString)
|
||||||
@ -170,7 +182,12 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
} else {
|
} else {
|
||||||
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
|
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
|
||||||
}
|
}
|
||||||
|
case .weather(interval: let interval, units: let units, api_key: let api_key, icon_type: let icon_type):
|
||||||
|
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type, onTap: action)
|
||||||
|
case .currency(interval: let interval, from: let from, to: let to):
|
||||||
|
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action)
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
|
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
|
||||||
widthBarItem.setWidth(value: value)
|
widthBarItem.setWidth(value: value)
|
||||||
}
|
}
|
||||||
@ -209,6 +226,14 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
task.arguments = parameters
|
task.arguments = parameters
|
||||||
task.launch()
|
task.launch()
|
||||||
}
|
}
|
||||||
|
case .openUrl(url: let url):
|
||||||
|
return {
|
||||||
|
if let url = URL(string: url), NSWorkspace.shared.open(url) {
|
||||||
|
// print("URL was successfully opened")
|
||||||
|
} else {
|
||||||
|
print("error", url)
|
||||||
|
}
|
||||||
|
}
|
||||||
case .custom(closure: let closure):
|
case .custom(closure: let closure):
|
||||||
return closure
|
return closure
|
||||||
case .none:
|
case .none:
|
||||||
|
|||||||
@ -14,9 +14,7 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
mScope: kAudioDevicePropertyScopeOutput,
|
mScope: kAudioDevicePropertyScopeOutput,
|
||||||
mElement: kAudioObjectPropertyElementMaster)
|
mElement: kAudioObjectPropertyElementMaster)
|
||||||
|
|
||||||
addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
|
AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock)
|
||||||
onAudioObjectID: defaultDeviceID,
|
|
||||||
forPropertyAddress: &forPropertyAddress)
|
|
||||||
|
|
||||||
if (image == nil) {
|
if (image == nil) {
|
||||||
sliderItem = CustomSlider()
|
sliderItem = CustomSlider()
|
||||||
@ -32,28 +30,10 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
self.view = sliderItem
|
self.view = sliderItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func addListenerBlock( listenerBlock: @escaping AudioObjectPropertyListenerBlock, onAudioObjectID: AudioObjectID, forPropertyAddress: UnsafePointer<AudioObjectPropertyAddress>) {
|
|
||||||
|
|
||||||
if (kAudioHardwareNoError != AudioObjectAddPropertyListenerBlock(onAudioObjectID, forPropertyAddress, nil, listenerBlock)) {
|
|
||||||
print("Error calling: AudioObjectAddPropertyListenerBlock") }
|
|
||||||
}
|
|
||||||
|
|
||||||
func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) {
|
func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) {
|
||||||
var index: UInt32 = 0
|
|
||||||
while index < numberAddresses {
|
|
||||||
let address: AudioObjectPropertyAddress = addresses[Int(index)]
|
|
||||||
switch address.mSelector {
|
|
||||||
case kAudioHardwareServiceDeviceProperty_VirtualMasterVolume:
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.sliderItem.floatValue = self.getInputGain() * 100
|
self.sliderItem.floatValue = self.getInputGain() * 100
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
|
|
||||||
print("We didn't expect this!")
|
|
||||||
|
|
||||||
}
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
|
|||||||
133
MTMR/WeatherBarItem.swift
Normal file
133
MTMR/WeatherBarItem.swift
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
//
|
||||||
|
// WeatherBarItem.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Daniel Apatin on 18.04.2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||||
|
private let dateFormatter = DateFormatter()
|
||||||
|
private var timer: Timer!
|
||||||
|
private var interval: TimeInterval!
|
||||||
|
private var units: String
|
||||||
|
private var api_key: String
|
||||||
|
private var units_str = "°F"
|
||||||
|
private var prev_location: CLLocation!
|
||||||
|
private var location: CLLocation!
|
||||||
|
private let iconsImages = ["01d": "☀️", "01n": "☀️", "02d": "⛅️", "02n": "⛅️", "03d": "☁️", "03n": "☁️", "04d": "☁️", "04n": "☁️", "09d": "⛅️", "09n": "⛅️", "10d": "🌦", "10n": "🌦", "11d": "🌩", "11n": "🌩", "13d": "❄️", "13n": "❄️", "50d": "🌫", "50n": "🌫"]
|
||||||
|
private let iconsText = ["01d": "☀", "01n": "☀", "02d": "☁", "02n": "☁", "03d": "☁", "03n": "☁", "04d": "☁", "04n": "☁", "09d": "☂", "09n": "☂", "10d": "☂", "10n": "☂", "11d": "☈", "11n": "☈", "13d": "☃", "13n": "☃", "50d": "♨", "50n": "♨"]
|
||||||
|
private var iconsSource: Dictionary<String, String>
|
||||||
|
|
||||||
|
private var manager:CLLocationManager!
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text", onTap: @escaping () -> ()) {
|
||||||
|
self.interval = interval
|
||||||
|
self.units = units
|
||||||
|
self.api_key = api_key
|
||||||
|
|
||||||
|
if self.units == "metric" {
|
||||||
|
units_str = "°C"
|
||||||
|
}
|
||||||
|
|
||||||
|
if icon_type == "images" {
|
||||||
|
iconsSource = iconsImages
|
||||||
|
} else {
|
||||||
|
iconsSource = iconsText
|
||||||
|
}
|
||||||
|
|
||||||
|
super.init(identifier: identifier, title: "⏳", onTap: onTap)
|
||||||
|
|
||||||
|
button.bezelColor = .clear
|
||||||
|
self.view = button
|
||||||
|
|
||||||
|
let status = CLLocationManager.authorizationStatus()
|
||||||
|
if status == .restricted || status == .denied {
|
||||||
|
print("User permission not given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !CLLocationManager.locationServicesEnabled() {
|
||||||
|
print("Location services not enabled");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(updateWeather), userInfo: nil, repeats: true)
|
||||||
|
|
||||||
|
manager = CLLocationManager()
|
||||||
|
manager.delegate = self
|
||||||
|
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
||||||
|
manager.startUpdatingLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func updateWeather() {
|
||||||
|
if self.location != nil {
|
||||||
|
let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(self.units)&appid=\(self.api_key)")!)
|
||||||
|
|
||||||
|
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
|
||||||
|
|
||||||
|
if error == nil {
|
||||||
|
do {
|
||||||
|
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
|
||||||
|
|
||||||
|
var temperature: Int!
|
||||||
|
var condition_icon = ""
|
||||||
|
|
||||||
|
if let main = json["main"] as? [String : AnyObject] {
|
||||||
|
if let temp = main["temp"] as? Double {
|
||||||
|
temperature = Int(temp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let weather = json["weather"] as? NSArray, let item = weather[0] as? NSDictionary {
|
||||||
|
let icon = item["icon"] as! String
|
||||||
|
if let test = self.iconsSource[icon] {
|
||||||
|
condition_icon = test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if temperature != nil {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.setWeather(text: "\(condition_icon) \(temperature!)\(self.units_str)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch let jsonError {
|
||||||
|
print(jsonError.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setWeather(text: String) {
|
||||||
|
button.title = text
|
||||||
|
}
|
||||||
|
|
||||||
|
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||||
|
let lastLocation = locations.last!
|
||||||
|
self.location = lastLocation
|
||||||
|
if prev_location == nil {
|
||||||
|
updateWeather()
|
||||||
|
}
|
||||||
|
prev_location = lastLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
||||||
|
print(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
||||||
|
// print("inside didChangeAuthorization ");
|
||||||
|
updateWeather()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user