mirror of
https://github.com/lihaoyun6/AirBattery.git
synced 2025-11-25 03:15:23 +08:00
fix panel layout; add more widgets; click to refresh widgets; set any update interval
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
18B8E5C92B83C9AD00A20D03 /* BatteryWidget2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B8E5C82B83C9AD00A20D03 /* BatteryWidget2.swift */; };
|
||||
18E6E36E2C0BA6A800A666CE /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 18E6E36D2C0BA6A800A666CE /* Sparkle */; };
|
||||
18E6E3702C0BA7D200A666CE /* Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E6E36F2C0BA7D200A666CE /* Sparkle.swift */; };
|
||||
18EF7E632C141CB70065A9E1 /* BatteryWidget3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7E622C1414A50065A9E1 /* BatteryWidget3.swift */; };
|
||||
18F615F32B763DD800873AA4 /* BLEBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F615F22B763DD800873AA4 /* BLEBattery.swift */; };
|
||||
18F615F92B76439300873AA4 /* AirBatteryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F615F82B76439300873AA4 /* AirBatteryModel.swift */; };
|
||||
18F615FB2B7676D200873AA4 /* Supports.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F615FA2B7676D200873AA4 /* Supports.swift */; };
|
||||
@@ -114,6 +115,7 @@
|
||||
18B8E5C82B83C9AD00A20D03 /* BatteryWidget2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryWidget2.swift; sourceTree = "<group>"; };
|
||||
18C7CFDE2AAF842E005898EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Base; path = Base.lproj/Credits.rtf; sourceTree = "<group>"; };
|
||||
18E6E36F2C0BA7D200A666CE /* Sparkle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sparkle.swift; sourceTree = "<group>"; };
|
||||
18EF7E622C1414A50065A9E1 /* BatteryWidget3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryWidget3.swift; sourceTree = "<group>"; };
|
||||
18F615F22B763DD800873AA4 /* BLEBattery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEBattery.swift; sourceTree = "<group>"; };
|
||||
18F615F82B76439300873AA4 /* AirBatteryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirBatteryModel.swift; sourceTree = "<group>"; };
|
||||
18F615FA2B7676D200873AA4 /* Supports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Supports.swift; sourceTree = "<group>"; };
|
||||
@@ -233,6 +235,7 @@
|
||||
180B1C0D2C12017400DDB570 /* AppIntent.swift */,
|
||||
18892A8F2B81F8320017377E /* BatteryWidget.swift */,
|
||||
18B8E5C82B83C9AD00A20D03 /* BatteryWidget2.swift */,
|
||||
18EF7E622C1414A50065A9E1 /* BatteryWidget3.swift */,
|
||||
18892A952B81F8330017377E /* Info.plist */,
|
||||
18892A962B81F8330017377E /* widget.entitlements */,
|
||||
);
|
||||
@@ -434,6 +437,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
18892AA22B82F0880017377E /* AirBatteryModel.swift in Sources */,
|
||||
18EF7E632C141CB70065A9E1 /* BatteryWidget3.swift in Sources */,
|
||||
18892AA32B82F09C0017377E /* InternalBattery.swift in Sources */,
|
||||
18892A8E2B81F8320017377E /* widgetBundle.swift in Sources */,
|
||||
18892A902B81F8320017377E /* BatteryWidget.swift in Sources */,
|
||||
@@ -492,7 +496,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = AirBatteryHelper/AirBatteryHelper.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 134;
|
||||
CURRENT_PROJECT_VERSION = 135;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"AirBatteryHelper/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = L4T783637F;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -507,7 +511,7 @@
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.3.4;
|
||||
MARKETING_VERSION = 1.3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.AirBatteryHelper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -526,7 +530,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = AirBatteryHelper/AirBatteryHelper.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 134;
|
||||
CURRENT_PROJECT_VERSION = 135;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"AirBatteryHelper/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = L4T783637F;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -541,7 +545,7 @@
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.3.4;
|
||||
MARKETING_VERSION = 1.3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.AirBatteryHelper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -678,7 +682,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = AirBattery/AirBattery.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 134;
|
||||
CURRENT_PROJECT_VERSION = 135;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"AirBattery/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = L4T783637F;
|
||||
@@ -695,7 +699,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.3.4;
|
||||
MARKETING_VERSION = 1.3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.AirBattery;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -712,7 +716,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = AirBattery/AirBattery.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 134;
|
||||
CURRENT_PROJECT_VERSION = 135;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"AirBattery/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = L4T783637F;
|
||||
@@ -729,7 +733,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.3.4;
|
||||
MARKETING_VERSION = 1.3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.AirBattery;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -745,7 +749,7 @@
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = widget/widget.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 134;
|
||||
CURRENT_PROJECT_VERSION = 135;
|
||||
DEVELOPMENT_TEAM = L4T783637F;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
@@ -761,7 +765,7 @@
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.3.4;
|
||||
MARKETING_VERSION = 1.3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.AirBattery.widget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -779,7 +783,7 @@
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = widget/widget.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 134;
|
||||
CURRENT_PROJECT_VERSION = 135;
|
||||
DEVELOPMENT_TEAM = L4T783637F;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
@@ -795,7 +799,7 @@
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.3.4;
|
||||
MARKETING_VERSION = 1.3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.AirBattery.widget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
||||
@@ -48,6 +48,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
||||
let magicBattery = MagicBattery()
|
||||
let ideviceBattery = IDeviceBattery()
|
||||
|
||||
func application(_ application: NSApplication, open urls: [URL]) {
|
||||
print(urls)
|
||||
}
|
||||
|
||||
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
// 用户点击 Dock 图标时会调用这个方法
|
||||
if showOn == "sbar" {
|
||||
@@ -73,7 +77,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
||||
switch dockOrientation {
|
||||
case "bottom":
|
||||
// Dock 位于屏幕底部
|
||||
menuX = menuX + 176 > visibleFrame.maxX ? visibleFrame.maxX - 176 : menuX - 176
|
||||
menuX = menuX + 186 > visibleFrame.maxX ? visibleFrame.maxX - 362 : menuX - 176
|
||||
if menuX + 186 > visibleFrame.maxX {
|
||||
menuX = visibleFrame.maxX - 362
|
||||
} else if menuX - 166 < visibleFrame.minX {
|
||||
menuX = visibleFrame.minX + 10
|
||||
} else {
|
||||
menuX = menuX - 176
|
||||
}
|
||||
menuY = max(menuY, visibleFrame.origin.y) + 20
|
||||
case "right":
|
||||
// Dock 位于屏幕右侧
|
||||
@@ -124,6 +135,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(handleURLEvent(_:replyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
|
||||
//if let window = NSApplication.shared.windows.first { window.close() }
|
||||
launchAtLogin = NSWorkspace.shared.runningApplications.contains { $0.bundleIdentifier == "com.lihaoyun6.AirBatteryHelper" }
|
||||
print("⚙️ Launch AirBattery at login = \(launchAtLogin)")
|
||||
@@ -226,6 +238,21 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
||||
statusMenu.removeAllItems()
|
||||
statusMenu.addItem(menuItem)
|
||||
}
|
||||
|
||||
@objc func handleURLEvent(_ event: NSAppleEventDescriptor, replyEvent: NSAppleEventDescriptor) {
|
||||
if let urlString = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject))?.stringValue,
|
||||
let url = URL(string: urlString) {
|
||||
if url.scheme == "airbattery"{
|
||||
switch url.host {
|
||||
case "reloadwingets" :
|
||||
print("Reloading all widgets...")
|
||||
AirBatteryModel.writeData()
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
default: print("Unknow command!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openAboutPanel() {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.lihaoyun6.AirBattery</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>airbattery</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://raw.githubusercontent.com/lihaoyun6/AirBattery/main/appcast.xml</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
|
||||
@@ -10,7 +10,7 @@ import UserNotifications
|
||||
|
||||
let dockTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
let alertTimer = Timer.publish(every: 300, on: .main, in: .common).autoconnect()
|
||||
let widgetDataTimer = Timer.publish(every: 29, on: .main, in: .common).autoconnect()
|
||||
let widgetDataTimer = Timer.publish(every: 25, on: .main, in: .common).autoconnect()
|
||||
let widgetInterval = UserDefaults.standard.integer(forKey: "widgetInterval")
|
||||
let updateInterval = UserDefaults.standard.double(forKey: "updateInterval")
|
||||
let widgetViewTimer = Timer.publish(every: TimeInterval(60 * (widgetInterval != 0 ? abs(widgetInterval) : Int(updateInterval))), on: .main, in: .common).autoconnect()
|
||||
|
||||
@@ -237,6 +237,7 @@ struct popover: View {
|
||||
var fromDock: Bool = false
|
||||
var allDevices: [Device]
|
||||
let hiddenDevices = AirBatteryModel.getBlackList()
|
||||
@State private var overReloadButton = false
|
||||
@State private var overCopyButton = false
|
||||
@State private var overHideButton = false
|
||||
@State private var overAlertButton = false
|
||||
|
||||
@@ -133,7 +133,7 @@ struct SettingsView: View {
|
||||
HStack{
|
||||
Toggle(isOn: $readIDevice) {}.toggleStyle(.switch)
|
||||
HStack(spacing: 2) {
|
||||
Text("WiFi / LAN Scanner")
|
||||
Text("WiFi Scanner (iOS)")
|
||||
SWInfoButton(showOnHover: false, fillMode: true, animatePopover: true, content: "Scan your iPhone / iPad / Apple Watch / VisionPro and other iDevices under the same router".local, primaryColor: NSColor(named: "my_blue") ?? NSColor.systemGray, preferredEdge: .minY)
|
||||
.frame(width: 19, height: 19)
|
||||
}
|
||||
@@ -141,7 +141,7 @@ struct SettingsView: View {
|
||||
HStack{
|
||||
Toggle(isOn: $ideviceOverBLE) {}.toggleStyle(.switch)
|
||||
HStack(spacing: 2) {
|
||||
Text("Bluetooth Scanner")
|
||||
Text("BLE Scanner (iOS)")
|
||||
SWInfoButton(showOnHover: false, fillMode: true, animatePopover: true, content: "Scan your iPhone and iPad(Cellular) via Bluetooth".local, primaryColor: NSColor(named: "my_blue") ?? NSColor.systemGray, preferredEdge: .minY)
|
||||
.frame(width: 19, height: 19)
|
||||
}
|
||||
@@ -157,22 +157,22 @@ struct SettingsView: View {
|
||||
}
|
||||
Spacer()
|
||||
Form(){
|
||||
HStack{
|
||||
Toggle(isOn: $readAirpods) {}.toggleStyle(.switch)
|
||||
Text("Find AirPods / Beats")
|
||||
}
|
||||
HStack{
|
||||
Toggle(isOn: $readBTDevice) {}.toggleStyle(.switch)
|
||||
HStack(spacing: 2) {
|
||||
Text("Non-Apple Devices")
|
||||
SWInfoButton(showOnHover: false, fillMode: true, animatePopover: true, content: "Only some of non-Apple peripherals are supported!".local, primaryColor: NSColor(named: "my_blue") ?? NSColor.systemGray, preferredEdge: .minY)
|
||||
Text("Bluetooth Scanner")
|
||||
SWInfoButton(showOnHover: false, fillMode: true, animatePopover: true, content: "Get the battery usage of some Bluetooth peripherals (mouse, keyboard, headphone, etc.)\n\nPlease Note: Some devices cannot be listed even though macOS knows their battery usage".local, primaryColor: NSColor(named: "my_blue") ?? NSColor.systemGray, preferredEdge: .minY)
|
||||
.frame(width: 19, height: 19)
|
||||
}
|
||||
}
|
||||
HStack{
|
||||
Toggle(isOn: $readAirpods) {}.toggleStyle(.switch)
|
||||
Text("Find AirPods / Beats")
|
||||
}
|
||||
HStack{
|
||||
Toggle(isOn: $readBLEDevice) {}.toggleStyle(.switch)
|
||||
HStack(spacing: 2) {
|
||||
Text("Get All BLE Devices")
|
||||
Text("Non-Apple BLE Devices")
|
||||
SWInfoButton(showOnHover: false, fillMode: true, animatePopover: true, content: "Try to get the battery usage of any Bluetooth device that AirBattery can find\n\nWARNING: This is a BETA feature and may cause unexpected errors!".local, primaryColor: NSColor(named: "my_yellow") ?? NSColor.systemGray, preferredEdge: .minY)
|
||||
.frame(width: 19, height: 19)
|
||||
}
|
||||
@@ -183,19 +183,29 @@ struct SettingsView: View {
|
||||
//Divider().frame(width: 440)
|
||||
HStack {
|
||||
Spacer()
|
||||
Picker("Update Interval", selection: $updateInterval) {
|
||||
Text("Short").tag(1.0)
|
||||
Text("Medium").tag(2.0)
|
||||
Text("Long").tag(3.0)
|
||||
}
|
||||
.frame(width: 270)
|
||||
HStack(spacing: 2) {
|
||||
Picker("Update Interval", selection: $updateInterval) {
|
||||
Text("Default".local).tag(1.0)
|
||||
Text("Custom".local).tag(updateInterval == 1.0 ? 2.0 : updateInterval)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.onChange(of: updateInterval) { _ in
|
||||
_ = createAlert(title: "Relaunch Required".local, message: "Restart AirBattery to apply this change.".local, button1: "OK".local).runModal()
|
||||
}
|
||||
TextField("", value: $updateInterval, formatter: NumberFormatter())
|
||||
.textFieldStyle(.squareBorder)
|
||||
.frame(width: 26)
|
||||
.onChange(of: updateInterval) { newValue in
|
||||
if newValue > 99.0 { updateInterval = 99.0 }
|
||||
if newValue < 1.0 { updateInterval = 1.0 }
|
||||
}
|
||||
.disabled(updateInterval == 1.0)
|
||||
Text("min")
|
||||
.foregroundColor(updateInterval == 1.0 ? .secondary : .primary)
|
||||
}.fixedSize()
|
||||
Spacer()
|
||||
HStack(spacing: 2) {
|
||||
Text("Merge Threshold".local).padding(.trailing, 5)
|
||||
Text("Merge Limits".local).padding(.trailing, 5)
|
||||
TextField("", value: $twsMerge, formatter: NumberFormatter())
|
||||
.textFieldStyle(.squareBorder)
|
||||
.frame(width: 26)
|
||||
@@ -287,15 +297,12 @@ struct SettingsView: View {
|
||||
Text("Reverse the Device List")
|
||||
}
|
||||
HStack(spacing: 3) {
|
||||
Text("Update Interval")
|
||||
SWInfoButton(showOnHover: false, fillMode: true, animatePopover: true, content: "System: Determined by macOS\n\nNearbility: Same as Nearbility setting".local, primaryColor: NSColor(named: "my_blue") ?? NSColor.systemGray, preferredEdge: .maxX)
|
||||
Text("Refresh Interval")
|
||||
SWInfoButton(showOnHover: false, fillMode: true, animatePopover: true, content: "System Default: Determined by macOS\n\nNearbility: Same as Nearbility setting".local, primaryColor: NSColor(named: "my_blue") ?? NSColor.systemGray, preferredEdge: .maxX)
|
||||
.frame(width: 19, height: 19)
|
||||
Picker("", selection: $widgetInterval) {
|
||||
Text("System").tag(-1)
|
||||
Text("System Default").tag(-1)
|
||||
Text("Nearbility").tag(0)
|
||||
Text("1 "+"min".local).tag(1)
|
||||
Text("3 "+"min".local).tag(3)
|
||||
Text("5 "+"min".local).tag(5)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.onChange(of: widgetInterval) { _ in
|
||||
@@ -315,14 +322,14 @@ struct SettingsView: View {
|
||||
}.onChange(of: deviceOnWidget) { _ in _ = AirBatteryModel.singleDeviceName() }
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
AirBatteryModel.writeData()
|
||||
_ = AirBatteryModel.singleDeviceName()
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}, label: {
|
||||
Text("Reload All Widgets")
|
||||
})}
|
||||
.foregroundColor(.orange)
|
||||
}).padding(.top, 14)
|
||||
}
|
||||
}
|
||||
.onAppear { devices = AirBatteryModel.getAll(noFilter: true).filter({ $0.hasBattery }).map({ $0.deviceName }) }
|
||||
.onReceive(dockTimer) { _ in
|
||||
|
||||
@@ -34,23 +34,25 @@
|
||||
"Carousel Mode:" = "轮播模式:";
|
||||
"On" = "启用";
|
||||
"Off" = "关闭";
|
||||
"WiFi / LAN Scanner" = "通过局域网搜索设备";
|
||||
"Bluetooth Scanner" = "通过蓝牙搜索设备";
|
||||
"WiFi Scanner (iOS)" = "通过局域网搜索 iOS 设备";
|
||||
"BLE Scanner (iOS)" = "通过蓝牙搜索 iOS 设备";
|
||||
"Bluetooth Scanner" = "搜索支持的蓝牙外设";
|
||||
"Find AirPods / Beats" = "搜索AirPods/Beats耳机";
|
||||
"Non-Apple Devices" = "搜索非 Apple 设备";
|
||||
"Guess Power Status" = "推测BLE设备充电状态";
|
||||
"Get All BLE Devices" = "搜索所有 BLE 设备";
|
||||
"Non-Apple BLE Devices" = "搜索第三方 BLE 设备";
|
||||
"Guess Power Status" = "推测 BLE 设备充电状态";
|
||||
"Get All BLE Devices" = "搜索所有蓝牙 BLE 设备";
|
||||
"after 20min" = "20分钟后";
|
||||
"after 40min" = "40分钟后";
|
||||
"min" = "分钟";
|
||||
"Remove Offline Device:" = "移除离线设备:";
|
||||
"Never" = "永不";
|
||||
"Hidden" = "隐藏";
|
||||
"AirBattery is not running\nLaunch the app to make the widget work." = "请先启动 AirBattery 主程序以使小组件正常工作.";
|
||||
"AirBattery is not running\nLaunch the app to make\nthe widget work." = "请先启动 AirBattery 主程序\n以使小组件正常工作.";
|
||||
"Displays battery usage for your devices from AirBattery." = "显示由 AirBattery 收集到的电池用量信息.";
|
||||
"More ways to displays battery usage for your devices from AirBattery." = "以更多方式展示由 AirBattery 收集到的电池用量信息";
|
||||
"Displays the battery usage of a specific device." = "显示某台特定设备的电池用量信息";
|
||||
"AirBattery is not running\nLaunch the app to make the widget work" = "请先启动 AirBattery 主程序以使小组件正常工作";
|
||||
"AirBattery is not running\nLaunch the app to make\nthe widget work" = "请先启动 AirBattery 主程序\n以使小组件正常工作";
|
||||
"Displays battery usage for your devices from AirBattery" = "显示由 AirBattery 收集到的电量信息";
|
||||
"More ways to displays battery usage for your devices" = "以更多方式展示所有设备的电量信息";
|
||||
"Displays battery usage for your devices without percentage" = "以纯图形展示所有设备的电量信息";
|
||||
"Displays the battery usage of a specific device" = "显示某台特定设备的电量信息";
|
||||
"Batteries" = "电量信息";
|
||||
"Hide This" = "隐藏此设备";
|
||||
"Hidden Device..." = "已隐藏的设备...";
|
||||
@@ -79,8 +81,9 @@
|
||||
"Fully Charged Threshold:" = "充电完成阈值:";
|
||||
"Select a Device in Preferences" = "请在设置中选择需要显示的设备";
|
||||
"Single Device Widget" = "单设备小组件";
|
||||
"Update Interval" = "更新间隔";
|
||||
"Merge Threshold" = "双耳合并阈值";
|
||||
"Update Interval" = "数据更新间隔";
|
||||
"Refresh Interval" = "内容刷新间隔";
|
||||
"Merge Limits" = "耳机合并百分比";
|
||||
"Short" = "较短";
|
||||
"Medium" = "中等";
|
||||
"Long" = "较长";
|
||||
@@ -98,17 +101,19 @@
|
||||
"Scan your iPhone / iPad / Apple Watch / VisionPro and other iDevices under the same router" = "尝试搜索当前局域网中所有属于您的 iPhone / iPad / Apple Watch / VisionPro 等设备";
|
||||
"Scan your iPhone and iPad(Cellular) via Bluetooth" = "尝试通过蓝牙搜索周边属于您的 iPhone 或蜂窝版 iPad";
|
||||
"Guess if the iPhone / iPad or BLE device found by Bluetooth is charging" = "利用电量变化来推测通过蓝牙发现的 iPhone / iPad 或其他 BLE 设备";
|
||||
"Only some of non-Apple peripherals are supported!" = "仅支持部分第三方蓝牙设备";
|
||||
"Get the battery usage of some Bluetooth peripherals (mouse, keyboard, headphone, etc.)\n\nPlease Note: Some devices cannot be listed even though macOS knows their battery usage" = "获取某些受支持的蓝牙外设的电量信息 (鼠标、键盘、耳机等)\n\n请注意: 有些设备无法被列出, 即使你可以在 macOS 内置的小组件中看到它";
|
||||
"Try to get the battery usage of any Bluetooth device that AirBattery can find\n\nWARNING: This is a BETA feature and may cause unexpected errors!" = "尝试搜索周边所有的 BLE 设备, 即使它可能不属于您\n\n请注意: 这是一项测试版功能, 可能会导致未知的错误!";
|
||||
"Show or hide this Mac's built-in battery in the Dock icon" = "在 Dock 图标中显示此 Mac 的内置电池, 或将其从 Dock 图标上隐藏";
|
||||
"Cycle through all found devices in the Dock icon" = "在 Dock 图标中循环显示所有已发现的设备";
|
||||
"If the difference in battery usage between the left and right earbuds is less than this value, AirBattery will show them as one device" = "当同一副耳机左右两侧的电量差距小于此值的时候, AirBattery会将它们合并显示为一个设备";
|
||||
"System" = "系统默认";
|
||||
"System: Determined by macOS\n\nNearbility: Same as Nearbility setting" = "系统默认: 由 macOS 决定小组件何时更新\n\nNearbility: 与 Nearbility 更新频率保持一致";
|
||||
"AirBattery battery usage widget" = "AirBattery 电池用量小组件";
|
||||
"System Default" = "跟随系统默认";
|
||||
"System Default: Determined by macOS\n\nNearbility: Same as Nearbility setting" = "跟随系统默认: 由 macOS 决定小组件何时更新\n\nNearbility: 与 Nearbility 更新频率保持一致";
|
||||
"AirBattery battery usage widget" = "AirBattery 电量小组件";
|
||||
"Configuration" = "配置项";
|
||||
"Enter Device Name" = "请输入设备名称";
|
||||
"Right click to configure" = "右键单击以配置此小组件";
|
||||
"Device Name Copied" = "设备名复制成功";
|
||||
"Device name: \"%@\" has been copied to the clipboard." = "设备名 \"%@\" 已成功拷贝至剪贴板";
|
||||
"Reload All Widgets" = "强制刷新所有小组件";
|
||||
"Reload All Widgets" = "刷新所有小组件";
|
||||
"Default" = "默认";
|
||||
"Custom" = "自定义";
|
||||
|
||||
@@ -22,7 +22,7 @@ struct ViewSizeTimelineProviderNew: AppIntentTimelineProvider {
|
||||
if context.family == .systemSmall || context.family == .systemMedium {
|
||||
while data.count < 8 { data.append(Device(hasBattery: false, deviceID: "", deviceType: "blank", deviceName: "", batteryLevel: 0, isCharging: 0, lastUpdate: 0.0)) }
|
||||
} else if context.family == .systemLarge {
|
||||
if data.count >= 8 { data = Array(data[0..<8]) }
|
||||
if data.count >= 11 { data = Array(data[0..<11]) }
|
||||
}
|
||||
return SimpleEntry(date: Date(), data: data, family: context.family, mainApp: mainApp, configuration: configuration)
|
||||
}
|
||||
@@ -36,7 +36,7 @@ struct ViewSizeTimelineProviderNew: AppIntentTimelineProvider {
|
||||
if context.family == .systemSmall || context.family == .systemMedium {
|
||||
while data.count < 8 { data.append(Device(hasBattery: false, deviceID: "", deviceType: "blank", deviceName: "", batteryLevel: 0, isCharging: 0, lastUpdate: 0.0)) }
|
||||
} else if context.family == .systemLarge {
|
||||
if data.count >= 8 { data = Array(data[0..<8]) }
|
||||
if data.count >= 11 { data = Array(data[0..<11]) }
|
||||
}
|
||||
entry = SimpleEntry(date: Date(), data: data, family: context.family, mainApp: mainApp, configuration: configuration)
|
||||
let entries: [SimpleEntry] = [entry]
|
||||
@@ -107,7 +107,7 @@ struct batteryWidgetEntryView : View {
|
||||
@unknown default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}.widgetURL(URL(string: "airbattery://reloadwingets"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ struct LargeWidgetView : View {
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp{
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work.")
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
@@ -151,35 +151,38 @@ struct LargeWidgetView : View {
|
||||
.padding(.horizontal, 15)
|
||||
} else {
|
||||
VStack(alignment:.leading) {
|
||||
ForEach(entry.data, id: \.self) { item in
|
||||
VStack{
|
||||
HStack() {
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
ForEach(entry.data.indices, id: \.self) { index in
|
||||
if index < 8 {
|
||||
let item = entry.data[index]
|
||||
VStack{
|
||||
HStack() {
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
//.foregroundColor(Color("black_white"))
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
Text("\(((Date().timeIntervalSince1970 - item.lastUpdate) / 60) > 10 ? "⚠︎ " : "")\(item.deviceName)")
|
||||
.font(.system(size: 11))
|
||||
.frame(height: 31, alignment: .center)
|
||||
.padding(.horizontal, 7)
|
||||
Spacer()
|
||||
if item.batteryLevel <= 10 {
|
||||
Text("\(item.batteryLevel)%") .font(.system(size: 11))
|
||||
.foregroundColor(Color("dark_my_red"))
|
||||
} else {
|
||||
Text("\(item.batteryLevel)%") .font(.system(size: 11))
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
Text("\(((Date().timeIntervalSince1970 - item.lastUpdate) / 60) > 10 ? "⚠︎ " : "")\(item.deviceName)")
|
||||
.font(.system(size: 11))
|
||||
.frame(height: 31, alignment: .center)
|
||||
.padding(.horizontal, 7)
|
||||
Spacer()
|
||||
if item.batteryLevel <= 10 {
|
||||
Text("\(item.batteryLevel)%") .font(.system(size: 11))
|
||||
.foregroundColor(Color("dark_my_red"))
|
||||
} else {
|
||||
Text("\(item.batteryLevel)%") .font(.system(size: 11))
|
||||
}
|
||||
|
||||
/*Image(getBatteryIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
*/
|
||||
BatteryView(item: item)
|
||||
.scaleEffect(0.76)
|
||||
}
|
||||
|
||||
/*Image(getBatteryIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
*/
|
||||
BatteryView(item: item)
|
||||
.scaleEffect(0.76)
|
||||
if index != 7 { Divider() }
|
||||
}
|
||||
if entry.data.firstIndex(of: item) != 7 { Divider() }
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
@@ -198,7 +201,7 @@ struct SmallWidgetView : View {
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp {
|
||||
Text("AirBattery is not running\nLaunch the app to make\nthe widget work.")
|
||||
Text("AirBattery is not running\nLaunch the app to make\nthe widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
@@ -357,7 +360,7 @@ struct MediumWidgetView : View {
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp{
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work.")
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
@@ -382,26 +385,25 @@ struct MediumWidgetView : View {
|
||||
VStack(spacing: 17){
|
||||
ZStack{
|
||||
Group {
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
Circle()
|
||||
.trim(from: 0.0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 0.5)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
Circle()
|
||||
.trim(from: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.001)), to: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.0005)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.shadow(color: .black, radius: lineWidth*0.76, x: 0, y: 0)
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
.clipShape( Circle().stroke(lineWidth: lineWidth) )
|
||||
Circle()
|
||||
.trim(from: item.batteryLevel > 50 ? 0.25 : 0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 1.0)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
Group {
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
Circle()
|
||||
.trim(from: 0.0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 0.5)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
Circle()
|
||||
.trim(from: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.001)), to: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.0005)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.shadow(color: .black, radius: lineWidth*0.76, x: 0, y: 0)
|
||||
.clipShape( Circle().stroke(lineWidth: lineWidth) )
|
||||
Circle()
|
||||
.trim(from: item.batteryLevel > 50 ? 0.25 : 0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 1.0)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
}.rotationEffect(Angle(degrees: 270.0))
|
||||
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
@@ -489,7 +491,7 @@ struct batteryWidget: Widget {
|
||||
.widgetBackground(Color("WidgetBackground"))
|
||||
}
|
||||
.configurationDisplayName("Batteries")
|
||||
.description("Displays battery usage for your devices from AirBattery.")
|
||||
.description("Displays battery usage for your devices from AirBattery")
|
||||
.disableContentMarginsIfNeeded()
|
||||
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
|
||||
}
|
||||
|
||||
@@ -8,13 +8,94 @@
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct LargeWidgetView2: View {
|
||||
var entry: ViewSizeTimelineProvider.Entry
|
||||
let lineWidth = 6.0
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp{
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
} else {
|
||||
if entry.data.count == 0 {
|
||||
VStack(alignment:.leading) {
|
||||
ForEach(0..<11) { index in
|
||||
VStack{
|
||||
HStack() {
|
||||
Image("blank")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
Text(" ")
|
||||
.font(.system(size: 11))
|
||||
.frame(height: 20, alignment: .center)
|
||||
.padding(.horizontal, 7)
|
||||
Spacer()
|
||||
Text(" ")
|
||||
.font(.system(size: 11))
|
||||
Image("blank")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
}
|
||||
if index != 10 { Divider().padding(.top, -2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 15)
|
||||
} else {
|
||||
VStack(alignment:.leading) {
|
||||
ForEach(entry.data.indices, id: \.self) { index in
|
||||
let item = entry.data[index]
|
||||
VStack{
|
||||
HStack() {
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
Text("\(((Date().timeIntervalSince1970 - item.lastUpdate) / 60) > 10 ? "⚠︎ " : "")\(item.deviceName)")
|
||||
.font(.system(size: 11))
|
||||
.frame(height: 20, alignment: .center)
|
||||
.padding(.horizontal, 7)
|
||||
Spacer()
|
||||
if item.batteryLevel <= 10 {
|
||||
Text("\(item.batteryLevel)%") .font(.system(size: 11))
|
||||
.foregroundColor(Color("dark_my_red"))
|
||||
} else {
|
||||
Text("\(item.batteryLevel)%") .font(.system(size: 11))
|
||||
}
|
||||
|
||||
/*Image(getBatteryIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
*/
|
||||
BatteryView(item: item)
|
||||
.scaleEffect(0.76)
|
||||
}
|
||||
if index != 10 { Divider().padding(.top, -2) }
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.offset(y:4)
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 18)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct doubleRowBatteryWidgetEntryView: View {
|
||||
var entry: ViewSizeTimelineProvider.Entry
|
||||
let lineWidth = 6.0
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp{
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work.")
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
@@ -178,7 +259,7 @@ struct singleBatteryWidgetEntryView: View {
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp{
|
||||
Text("AirBattery is not running\nLaunch the app to make\nthe widget work.")
|
||||
Text("AirBattery is not running\nLaunch the app to make\nthe widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
@@ -283,13 +364,13 @@ struct batteryWidgetEntryView2: View {
|
||||
case .systemMedium:
|
||||
doubleRowBatteryWidgetEntryView(entry: entry)
|
||||
case .systemLarge:
|
||||
EmptyView()
|
||||
LargeWidgetView2(entry: entry)
|
||||
case .systemExtraLarge:
|
||||
EmptyView()
|
||||
@unknown default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}.widgetURL(URL(string: "airbattery://reloadwingets"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +386,7 @@ struct batteryWidget2New: Widget {
|
||||
.widgetBackground(Color("WidgetBackground"))
|
||||
}
|
||||
.configurationDisplayName("Batteries")
|
||||
.description("Displays the battery usage of a specific device.")
|
||||
.description("Displays the battery usage of a specific device")
|
||||
.disableContentMarginsIfNeeded()
|
||||
.supportedFamilies([.systemSmall])
|
||||
}
|
||||
@@ -322,7 +403,7 @@ struct batteryWidget2: Widget {
|
||||
.widgetBackground(Color("WidgetBackground"))
|
||||
}
|
||||
.configurationDisplayName("Batteries")
|
||||
.description("More ways to displays battery usage for your devices from AirBattery.")
|
||||
.description("More ways to displays battery usage for your devices")
|
||||
.disableContentMarginsIfNeeded()
|
||||
.supportFamily()
|
||||
}
|
||||
|
||||
316
widget/BatteryWidget3.swift
Normal file
316
widget/BatteryWidget3.swift
Normal file
@@ -0,0 +1,316 @@
|
||||
//
|
||||
// BatteryWidget3.swift
|
||||
// AirBattery
|
||||
//
|
||||
// Created by apple on 2024/6/8.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct batteryWidgetEntryView3 : View {
|
||||
var entry: ViewSizeTimelineProvider.Entry
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
switch entry.family {
|
||||
case .systemSmall:
|
||||
SmallWidgetView2(entry: entry)
|
||||
case .systemMedium:
|
||||
doubleBatteryWidgetEntryView2(entry: entry)
|
||||
case .systemLarge:
|
||||
EmptyView()
|
||||
case .systemExtraLarge:
|
||||
EmptyView()
|
||||
@unknown default:
|
||||
EmptyView()
|
||||
}
|
||||
}.widgetURL(URL(string: "airbattery://reloadwingets"))
|
||||
}
|
||||
}
|
||||
|
||||
struct SmallWidgetView2: View {
|
||||
var entry: ViewSizeTimelineProvider.Entry
|
||||
let lineWidth = 6.0
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp {
|
||||
Text("AirBattery is not running\nLaunch the app to make\nthe widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
} else {
|
||||
if entry.data.count == 0 {
|
||||
VStack(spacing: 17) {
|
||||
HStack(spacing: 17) {
|
||||
ForEach(0..<2) { index in
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
}
|
||||
}
|
||||
HStack(spacing: 17) {
|
||||
ForEach(0..<2) { index in
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
VStack(spacing: 17) {
|
||||
HStack(spacing: 17){
|
||||
ForEach(entry.data[0..<2], id: \.self) { item in
|
||||
ZStack{
|
||||
Group {
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
Circle()
|
||||
.trim(from: 0.0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 0.5)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
Circle()
|
||||
.trim(from: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.001)), to: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.0005)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.shadow(color: .black, radius: lineWidth*0.76, x: 0, y: 0)
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
.clipShape( Circle().stroke(lineWidth: lineWidth) )
|
||||
Circle()
|
||||
.trim(from: item.batteryLevel > 50 ? 0.25 : 0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 1.0)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 26, height: 26, alignment: .center)
|
||||
|
||||
if item.isCharging != 0 {
|
||||
Image("batt_bolt_mask")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 12, alignment: .center)
|
||||
.blendMode(.destinationOut)
|
||||
.offset(y:-29.5)
|
||||
Image("batt_bolt")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 10, alignment: .center)
|
||||
.foregroundColor(item.batteryLevel == 100 ? Color("my_green") : Color.primary)
|
||||
.offset(y:-29.5)
|
||||
}
|
||||
}.frame(width: 58, height: 58, alignment: .center)
|
||||
}.compositingGroup()
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 17){
|
||||
ForEach(entry.data[2..<4], id: \.self) { item in
|
||||
ZStack{
|
||||
Group {
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
Circle()
|
||||
.trim(from: 0.0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 0.5)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
Circle()
|
||||
.trim(from: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.001)), to: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.0005)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.shadow(color: .black, radius: lineWidth*0.76, x: 0, y: 0)
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
.clipShape( Circle().stroke(lineWidth: lineWidth) )
|
||||
Circle()
|
||||
.trim(from: item.batteryLevel > 50 ? 0.25 : 0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 1.0)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 26, height: 26, alignment: .center)
|
||||
|
||||
if item.isCharging != 0 {
|
||||
Image("batt_bolt_mask")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 12, alignment: .center)
|
||||
.blendMode(.destinationOut)
|
||||
.offset(y:-29.5)
|
||||
Image("batt_bolt")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 10, alignment: .center)
|
||||
.foregroundColor(item.batteryLevel == 100 ? Color("my_green") : Color.primary)
|
||||
.offset(y:-29.5)
|
||||
}
|
||||
}.frame(width: 58, height: 58, alignment: .center)
|
||||
}.compositingGroup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct doubleBatteryWidgetEntryView2: View {
|
||||
var entry: ViewSizeTimelineProvider.Entry
|
||||
let lineWidth = 6.0
|
||||
|
||||
var body: some View {
|
||||
if !entry.mainApp{
|
||||
Text("AirBattery is not running\nLaunch the app to make the widget work")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundColor(Color.gray)
|
||||
} else {
|
||||
if entry.data.count == 0 {
|
||||
VStack(spacing: 17){
|
||||
HStack(spacing: 23) {
|
||||
ForEach(0..<4) { index in
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.frame(width: 58, alignment: .center)
|
||||
}
|
||||
}
|
||||
HStack(spacing: 23) {
|
||||
ForEach(0..<4) { index in
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.frame(width: 58, alignment: .center)
|
||||
}
|
||||
}
|
||||
}.opacity(0.15)
|
||||
} else {
|
||||
VStack(spacing: 17){
|
||||
HStack(spacing: 23) {
|
||||
ForEach(entry.data[0..<4], id: \.self) { item in
|
||||
VStack(spacing: 17){
|
||||
ZStack{
|
||||
Group {
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
Circle()
|
||||
.trim(from: 0.0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 0.5)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
Circle()
|
||||
.trim(from: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.001)), to: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.0005)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.shadow(color: .black, radius: lineWidth*0.76, x: 0, y: 0)
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
.clipShape( Circle().stroke(lineWidth: lineWidth) )
|
||||
Circle()
|
||||
.trim(from: item.batteryLevel > 50 ? 0.25 : 0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 1.0)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 26, height: 26, alignment: .center)
|
||||
|
||||
if item.isCharging != 0 {
|
||||
Image("batt_bolt_mask")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 12, alignment: .center)
|
||||
.blendMode(.destinationOut)
|
||||
.offset(y:-29.5)
|
||||
Image("batt_bolt")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 10, alignment: .center)
|
||||
.foregroundColor(item.batteryLevel == 100 ? Color("my_green") : Color.primary)
|
||||
.offset(y:-29.5)
|
||||
}
|
||||
}.frame(width: 58, height: 58, alignment: .center)
|
||||
}.compositingGroup()
|
||||
}
|
||||
}
|
||||
}
|
||||
HStack(spacing: 23) {
|
||||
ForEach(entry.data[4..<8], id: \.self) { item in
|
||||
VStack(spacing: 17){
|
||||
ZStack{
|
||||
Group {
|
||||
Circle()
|
||||
.stroke(lineWidth: lineWidth)
|
||||
.opacity(0.15)
|
||||
Circle()
|
||||
.trim(from: 0.0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 0.5)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
Circle()
|
||||
.trim(from: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.001)), to: CGFloat(abs((min(Double(item.batteryLevel)/100.0, 1.0))-0.0005)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.shadow(color: .black, radius: lineWidth*0.76, x: 0, y: 0)
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
.clipShape( Circle().stroke(lineWidth: lineWidth) )
|
||||
Circle()
|
||||
.trim(from: item.batteryLevel > 50 ? 0.25 : 0, to: CGFloat(min(Double(item.batteryLevel)/100.0, 1.0)))
|
||||
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
|
||||
.foregroundColor(Color(getPowerColor(item.batteryLevel)))
|
||||
.rotationEffect(Angle(degrees: 270.0))
|
||||
|
||||
Image(getDeviceIcon(item))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 26, height: 26, alignment: .center)
|
||||
|
||||
if item.isCharging != 0 {
|
||||
Image("batt_bolt_mask")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 12, alignment: .center)
|
||||
.blendMode(.destinationOut)
|
||||
.offset(y:-29.5)
|
||||
Image("batt_bolt")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 10, alignment: .center)
|
||||
.foregroundColor(item.batteryLevel == 100 ? Color("my_green") : Color.primary)
|
||||
.offset(y:-29.5)
|
||||
}
|
||||
}.frame(width: 58, height: 58, alignment: .center)
|
||||
}.compositingGroup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct batteryWidget3: Widget {
|
||||
let kind: String = "widget.battery.part4"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: ViewSizeTimelineProvider()) { entry in
|
||||
batteryWidgetEntryView3(entry: entry)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.ignoresSafeArea()
|
||||
.widgetBackground(Color("WidgetBackground"))
|
||||
}
|
||||
.configurationDisplayName("Batteries")
|
||||
.description("Displays battery usage for your devices without percentage")
|
||||
.disableContentMarginsIfNeeded()
|
||||
.supportedFamilies([.systemMedium, .systemSmall])
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,9 @@ extension WidgetConfiguration {
|
||||
|
||||
func supportFamily() -> some WidgetConfiguration {
|
||||
if #available(macOS 14, *) {
|
||||
return self.supportedFamilies([.systemMedium])
|
||||
return self.supportedFamilies([.systemLarge, .systemMedium])
|
||||
} else {
|
||||
return self.supportedFamilies([.systemMedium, .systemSmall])
|
||||
return self.supportedFamilies([.systemLarge, .systemMedium, .systemSmall])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,9 @@ struct widgetBundle: WidgetBundle {
|
||||
|
||||
func widgets() -> some Widget {
|
||||
if #available(macOS 14, *) {
|
||||
return WidgetBundleBuilder.buildBlock(batteryWidget(), batteryWidget2(), batteryWidget2New())
|
||||
return WidgetBundleBuilder.buildBlock(batteryWidget(), batteryWidget2New(), batteryWidget2(), batteryWidget3())
|
||||
} else {
|
||||
return WidgetBundleBuilder.buildBlock(batteryWidget(), batteryWidget2())
|
||||
return WidgetBundleBuilder.buildBlock(batteryWidget(), batteryWidget2(), batteryWidget3())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user