diff --git a/README.md b/README.md
index f7625a08b..4fbabf695 100644
--- a/README.md
+++ b/README.md
@@ -47,26 +47,26 @@ If ServerBox app has any bug, please open an [issue](https://github.com/lollipop
diff --git a/README_zh.md b/README_zh.md
index a1058792b..5cfad8726 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -47,26 +47,26 @@
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 46db87b9d..0a6fa9f6a 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -586,7 +586,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -596,7 +596,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -720,7 +720,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -730,7 +730,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -748,7 +748,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -758,7 +758,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -779,7 +779,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -792,7 +792,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -818,7 +818,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -831,7 +831,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -854,7 +854,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -867,7 +867,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -890,7 +890,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -902,7 +902,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -931,7 +931,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -943,7 +943,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
@@ -969,7 +969,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 588;
+ CURRENT_PROJECT_VERSION = 589;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -981,7 +981,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.588;
+ MARKETING_VERSION = 1.0.589;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
diff --git a/ios/Runner/Utils.swift b/ios/Runner/Utils.swift
index 814cb7a07..9df0472a9 100644
--- a/ios/Runner/Utils.swift
+++ b/ios/Runner/Utils.swift
@@ -7,6 +7,8 @@
import Foundation
+let accessoryKey = "accessory_widget_url"
+
extension Date {
func toStr() -> String {
let formatter = DateFormatter()
diff --git a/ios/StatusWidget/StatusWidget.swift b/ios/StatusWidget/StatusWidget.swift
index 4900804ad..8743d9bdd 100644
--- a/ios/StatusWidget/StatusWidget.swift
+++ b/ios/StatusWidget/StatusWidget.swift
@@ -8,10 +8,11 @@
import WidgetKit
import SwiftUI
import Intents
+import Foundation
let demoStatus = Status(name: "Server", cpu: "31.7%", mem: "1.3g / 1.9g", disk: "7.1g / 30.0g", net: "712.3k / 1.2m")
let domain = "com.lollipopkit.toolbox"
-var url: String?
+let bgColor = DynamicColor(dark: UIColor.black, light: UIColor.white)
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
@@ -24,11 +25,18 @@ struct Provider: IntentTimelineProvider {
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
- url = configuration.url
+ var url = configuration.url
+
+ @Environment(\.widgetFamily) var family: WidgetFamily
+ if #available(iOSApplicationExtension 16.0, *) {
+ if family == .accessoryInline || family == .accessoryRectangular {
+ url = UserDefaults.standard.string(forKey: accessoryKey)
+ }
+ }
let currentDate = Date()
let refreshDate = Calendar.current.date(byAdding: .minute, value: 30, to: currentDate)!
- StatusLoader.fetch { result in
+ StatusLoader.fetch(url: url) { result in
let entry: SimpleEntry
switch result {
case .success(let status):
@@ -55,36 +63,61 @@ struct SimpleEntry: TimelineEntry {
struct StatusWidgetEntryView : View {
var entry: Provider.Entry
+ @Environment(\.widgetFamily) var family: WidgetFamily
+
var body: some View {
switch entry.state {
case .loading:
- ProgressView().padding()
+ ProgressView().widgetBackground()
case .error(let descriotion):
- Text(descriotion).padding(.all, 13)
+ Text(descriotion).widgetBackground()
case .normal(let data):
let sumColor: Color = .primary.opacity(0.7)
- VStack(alignment: .leading, spacing: 3.7) {
- Text(data.name).font(.system(.title3, design: .monospaced))
- Spacer()
- DetailItem(icon: "cpu", text: data.cpu, color: sumColor)
- DetailItem(icon: "memorychip", text: data.mem, color: sumColor)
- DetailItem(icon: "externaldrive", text: data.disk, color: sumColor)
- DetailItem(icon: "network", text: data.net, color: sumColor)
- Spacer()
- DetailItem(icon: "clock", text: entry.date.toStr(), color: sumColor)
+ switch family {
+ case .accessoryRectangular:
+ VStack(alignment: .leading, spacing: 2) {
+ HStack {
+ Text(data.name)
+ .font(.system(size: 15, weight: .semibold, design: .monospaced))
+ Spacer()
+ CirclePercent(percent: data.cpu)
+ .padding(.top, 3)
+ .padding(.trailing, 3)
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+ Spacer()
+ DetailItem(icon: "memorychip", text: data.mem, color: sumColor)
+ DetailItem(icon: "externaldrive", text: data.disk, color: sumColor)
+ DetailItem(icon: "network", text: data.net, color: sumColor)
+ }
+ .widgetBackground()
+ case .accessoryInline:
+ Text("\(data.name) \(data.cpu)").widgetBackground()
+ default:
+ VStack(alignment: .leading, spacing: 3.7) {
+ Text(data.name).font(.system(.title3, design: .monospaced))
+ Spacer()
+ DetailItem(icon: "cpu", text: data.cpu, color: sumColor)
+ DetailItem(icon: "memorychip", text: data.mem, color: sumColor)
+ DetailItem(icon: "externaldrive", text: data.disk, color: sumColor)
+ DetailItem(icon: "network", text: data.net, color: sumColor)
+ Spacer()
+ DetailItem(icon: "clock", text: entry.date.toStr(), color: sumColor)
+ .padding(.bottom, 3)
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+ .autoPadding()
+ .widgetBackground()
}
- .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
- .autoPadding()
- .widgetBackground()
}
}
}
extension View {
- // Set bg to Color.black in Night, Color.white in Day
@ViewBuilder
func widgetBackground() -> some View {
- let backgroundView = Color(UIColor.systemBackground)
+ // Set bg to black in Night, white in Day
+ let backgroundView = Color(bgColor.resolve())
if #available(iOS 17.0, *) {
@Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground
containerBackground(for: .widget) {
@@ -103,10 +136,6 @@ extension View {
// iOS 17 will auto add a SafeArea, so when iOS < 17, add .padding(.all, 17)
func autoPadding() -> some View {
if #available(iOS 17.0, *) {
- @Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground
- if (showsWidgetContainerBackground) {
- return self.padding(.all, 17)
- }
return self
} else {
return self.padding(.all, 17)
@@ -123,11 +152,12 @@ struct StatusWidget: Widget {
}
.configurationDisplayName("Status")
.description("Status of your servers.")
- .supportedFamilies([.systemSmall])
- if #available(iOS 16.0, *) {
- let _ = cfg.supportedFamilies([.accessoryInline, .accessoryCircular])
+
+ if #available(iOSApplicationExtension 16.0, *) {
+ return cfg.supportedFamilies([.systemSmall, .accessoryRectangular, .accessoryInline])
+ } else {
+ return cfg.supportedFamilies([.systemSmall])
}
- return cfg
}
}
@@ -139,7 +169,7 @@ struct StatusWidget_Previews: PreviewProvider {
}
struct StatusLoader {
- static func fetch(completion: @escaping (Result) -> Void) {
+ static func fetch(url: String?, completion: @escaping (Result) -> Void) {
guard let url = url, url.count >= 12 else {
completion(.failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "https://github.com/lollipopkit/server_box_monitor/wiki"])))
return
@@ -148,6 +178,9 @@ struct StatusLoader {
completion(.failure(NSError(domain: domain, code: 1, userInfo: [NSLocalizedDescriptionKey: "url is invalid"])))
return
}
+
+ UserDefaults.standard.set(url.absoluteString, forKey: accessoryKey)
+
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
completion(.failure(error!))
@@ -173,7 +206,7 @@ struct StatusLoader {
if (code != 0) {
switch (code) {
default:
- let msg = jsonAll["msg"] as? String ?? ""
+ let msg = jsonAll["msg"] as? String ?? "Empty err"
return .failure(NSError(domain: domain, code: code, userInfo: [NSLocalizedDescriptionKey: msg]))
}
}
@@ -202,3 +235,49 @@ struct DetailItem: View {
}
}
}
+
+struct DetailItemSmall: View {
+ let icon: String
+ let text: String
+ let color: Color
+
+ var body: some View {
+ HStack(spacing: 6.7) {
+ Image(systemName: icon).resizable().foregroundColor(color).frame(width: 11, height: 11, alignment: .center)
+ Text(text)
+ .font(.system(size: 11, design: .monospaced))
+ .foregroundColor(color)
+ }
+ }
+}
+
+// 空心圆,显示百分比
+struct CirclePercent: View {
+ // eg: 31.7%
+ let percent: String
+
+ var body: some View {
+ // 31.7% -> 0.317
+ let percentD = Double(percent.trimmingCharacters(in: .init(charactersIn: "%")))
+ let double = (percentD ?? 0) / 100
+ Circle()
+ .trim(from: 0, to: CGFloat(double))
+ .stroke(Color.primary, lineWidth: 3)
+ .animation(.easeInOut(duration: 0.5))
+ }
+}
+
+struct DynamicColor {
+ let dark: UIColor
+ let light: UIColor
+
+ func resolve() -> UIColor {
+ if #available(iOS 13, *) { // 版本号大于等于13
+ return UIColor { (traitCollection: UITraitCollection) -> UIColor in
+ return traitCollection.userInterfaceStyle == UIUserInterfaceStyle.dark ?
+ self.dark : self.light
+ }
+ }
+ return self.light
+ }
+}
diff --git a/lib/data/model/server/system.dart b/lib/data/model/server/system.dart
index ce41cada8..4455d7faf 100644
--- a/lib/data/model/server/system.dart
+++ b/lib/data/model/server/system.dart
@@ -9,15 +9,16 @@ enum SystemType {
const SystemType._(this.value);
- static SystemType? parse(String? value) {
- if (value == null) return null;
- switch (value) {
+ static SystemType parse(String value) {
+ switch (value.trim()) {
case linuxSign:
return SystemType.linux;
case bsdSign:
return SystemType.bsd;
+ default:
+ // Fallback to linux
+ return SystemType.linux;
}
- return null;
}
bool isSegmentsLenMatch(int len) => len == segmentsLen;
diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart
index 639e49239..798394709 100644
--- a/lib/data/provider/server.dart
+++ b/lib/data/provider/server.dart
@@ -329,10 +329,10 @@ class ServerProvider extends ChangeNotifier {
}
final systemType = SystemType.parse(segments[0]);
- if (systemType == null || !systemType.isSegmentsLenMatch(segments.length)) {
+ if (!systemType.isSegmentsLenMatch(segments.length)) {
_limiter.inc(sid);
s.status.failedInfo =
- 'Segments not match: expect ${systemType?.segmentsLen}, got ${segments.length}';
+ 'Segments not match: expect ${systemType.segmentsLen}, got ${segments.length}';
_setServerState(s, ServerState.failed);
return;
}
diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart
index 17d2c5a97..7f9339e2b 100644
--- a/lib/data/res/build_data.dart
+++ b/lib/data/res/build_data.dart
@@ -2,9 +2,9 @@
class BuildData {
static const String name = "ServerBox";
- static const int build = 588;
+ static const int build = 589;
static const String engine = "3.13.6";
- static const String buildAt = "2023-10-13 14:38:17";
- static const int modifications = 5;
+ static const String buildAt = "2023-10-14 12:09:35";
+ static const int modifications = 6;
static const int script = 21;
}