fix(ios): device status leaves battery monitoring enabled (#128174)

* fix(ios): restore battery monitoring after device status

* fix(ios): preserve native battery monitoring ownership

* test(ios): make battery ownership regression deterministic

* fix(ios): avoid Swift Testing key-path inference failure
This commit is contained in:
xingzhou
2026-08-24 14:12:34 +08:00
committed by GitHub
parent d1c86cf8c3
commit 446c8c131a
3 changed files with 46 additions and 4 deletions
@@ -11,7 +11,7 @@ final class DeviceStatusService: DeviceStatusServicing {
}
func status() async throws -> OpenClawDeviceStatusPayload {
let battery = self.batteryStatus()
let battery = Self.batteryStatus(device: UIDevice.current)
let thermal = self.thermalStatus()
let storage = self.storageStatus()
let network = await self.networkStatus.currentStatus()
@@ -40,9 +40,10 @@ final class DeviceStatusService: DeviceStatusServicing {
locale: locale)
}
private func batteryStatus() -> OpenClawBatteryStatusPayload {
let device = UIDevice.current
static func batteryStatus(device: UIDevice) -> OpenClawBatteryStatusPayload {
let wasMonitoring = device.isBatteryMonitoringEnabled
device.isBatteryMonitoringEnabled = true
defer { device.isBatteryMonitoringEnabled = wasMonitoring }
let level = device.batteryLevel >= 0 ? Double(device.batteryLevel) : nil
let state: OpenClawBatteryState = switch device.batteryState {
case .charging: .charging
+40 -1
View File
@@ -974,7 +974,47 @@ private func overrideNotificationServingPreference(_ enabled: Bool) -> () -> Voi
}
}
@MainActor
private final class BatteryMonitoringDevice: UIDevice {
private var monitoringEnabled: Bool
private(set) var monitoringStatesDuringBatteryReads: [Bool] = []
init(monitoringEnabled: Bool) {
self.monitoringEnabled = monitoringEnabled
super.init()
}
override var isBatteryMonitoringEnabled: Bool {
get { self.monitoringEnabled }
set { self.monitoringEnabled = newValue }
}
override var batteryLevel: Float {
self.monitoringStatesDuringBatteryReads.append(self.monitoringEnabled)
return 0.5
}
override var batteryState: UIDevice.BatteryState {
self.monitoringStatesDuringBatteryReads.append(self.monitoringEnabled)
return .charging
}
}
@Suite(.serialized) struct NodeAppModelInvokeTests {
@Test @MainActor func `device status battery snapshot preserves monitoring ownership`() {
for initial in [false, true] {
let device = BatteryMonitoringDevice(monitoringEnabled: initial)
let payload = DeviceStatusService.batteryStatus(device: device)
#expect(payload.level == 0.5)
#expect(payload.state == .charging)
#expect(!device.monitoringStatesDuringBatteryReads.isEmpty)
#expect(device.monitoringStatesDuringBatteryReads.allSatisfy { $0 })
#expect(device.isBatteryMonitoringEnabled == initial)
}
}
@Test @MainActor func `health summary routes a fixed period to the health service`() async throws {
let service = MockHealthSummaryService()
let appModel = NodeAppModel(healthSummaryService: service)
@@ -7542,5 +7582,4 @@ private func overrideNotificationServingPreference(_ enabled: Bool) -> () -> Voi
try await appModel.sendVoiceTranscript(text: "hello", sessionKey: "main")
}
}
}
@@ -688,7 +688,9 @@ final class WatchDirectNode {
private func deviceStatus() -> OpenClawDeviceStatusPayload {
let device = WKInterfaceDevice.current()
let wasMonitoring = device.isBatteryMonitoringEnabled
device.isBatteryMonitoringEnabled = true
defer { device.isBatteryMonitoringEnabled = wasMonitoring }
let batteryState: OpenClawBatteryState = switch device.batteryState {
case .charging: .charging
case .full: .full