From a4e1a3ba2e2385527cb38a7822f6b7916b9878c4 Mon Sep 17 00:00:00 2001 From: darken Date: Mon, 13 Apr 2026 17:13:25 +0200 Subject: [PATCH] fix(overview): Hide stale cached age on live cards --- .../main/ui/overview/cards/DualPodsCard.kt | 2 +- .../main/ui/overview/cards/SinglePodsCard.kt | 2 +- .../core/cache/DeviceStateCacheExtensions.kt | 12 +++++++++++ .../monitor/core/cache/ToCachedStateTest.kt | 20 +++++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt index f91f44ac..968bf095 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt @@ -215,7 +215,7 @@ fun DualPodsCard( } // Cached battery indicator - if (device.isBatteryCached && !device.isAapReady) { + if (device.isBatteryCached && !device.isLive) { Spacer(modifier = Modifier.height(4.dp)) Text( text = stringResource(R.string.battery_cached_label, device.cachedBatteryFormatted(now)), diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt index e8925453..955c3cdb 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt @@ -240,7 +240,7 @@ fun SinglePodsCard( } // Cached battery indicator - if (device.isBatteryCached && !device.isAapReady) { + if (device.isBatteryCached && !device.isLive) { Spacer(modifier = Modifier.height(4.dp)) Text( text = stringResource(R.string.battery_cached_label, device.cachedBatteryFormatted(now)), diff --git a/app/src/main/java/eu/darken/capod/monitor/core/cache/DeviceStateCacheExtensions.kt b/app/src/main/java/eu/darken/capod/monitor/core/cache/DeviceStateCacheExtensions.kt index e5337e06..cefbf599 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/cache/DeviceStateCacheExtensions.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/cache/DeviceStateCacheExtensions.kt @@ -58,6 +58,10 @@ fun PodDevice.toCachedState( private fun hasStateChanged(old: CachedDeviceState, new: CachedDeviceState): Boolean { if (Duration.between(old.lastSeenAt, new.lastSeenAt).abs() > Duration.ofMinutes(1)) return true + if (hasSlotTimestampChanged(old.left, new.left)) return true + if (hasSlotTimestampChanged(old.right, new.right)) return true + if (hasSlotTimestampChanged(old.case, new.case)) return true + if (hasSlotTimestampChanged(old.headset, new.headset)) return true return old.left?.percent != new.left?.percent || old.right?.percent != new.right?.percent || old.case?.percent != new.case?.percent @@ -70,3 +74,11 @@ private fun hasStateChanged(old: CachedDeviceState, new: CachedDeviceState): Boo || old.serialNumber != new.serialNumber || old.firmwareVersion != new.firmwareVersion } + +private fun hasSlotTimestampChanged( + old: CachedBatterySlot?, + new: CachedBatterySlot?, +): Boolean { + if (old == null || new == null) return false + return Duration.between(old.updatedAt, new.updatedAt).abs() > Duration.ofMinutes(1) +} diff --git a/app/src/test/java/eu/darken/capod/monitor/core/cache/ToCachedStateTest.kt b/app/src/test/java/eu/darken/capod/monitor/core/cache/ToCachedStateTest.kt index c6c8aef6..4b85ee9d 100644 --- a/app/src/test/java/eu/darken/capod/monitor/core/cache/ToCachedStateTest.kt +++ b/app/src/test/java/eu/darken/capod/monitor/core/cache/ToCachedStateTest.kt @@ -134,5 +134,25 @@ class ToCachedStateTest : BaseTest() { ) liveDevice().toCachedState(existing, now).shouldNotBeNull() } + + @Test + fun `returns new state when a live slot refreshes an old cached timestamp`() { + val existing = CachedDeviceState( + profileId = "test-profile", + model = PodModel.AIRPODS_PRO3, + left = CachedDeviceState.CachedBatterySlot(0.8f, now.minusSeconds(10)), + right = CachedDeviceState.CachedBatterySlot(0.7f, now.minusSeconds(10)), + case = CachedDeviceState.CachedBatterySlot(0.5f, now.minusSeconds(177 * 60 * 60)), + isLeftCharging = false, + isRightCharging = false, + isCaseCharging = false, + isHeadsetCharging = false, + lastSeenAt = now.minusSeconds(10), + ) + + liveDevice(leftBattery = 0.8f, rightBattery = 0.7f, caseBattery = 0.5f) + .toCachedState(existing, now) + .shouldNotBeNull() + } } }