fix: Hide charging indicator for disconnected devices

This commit is contained in:
darken
2026-07-03 12:43:33 +02:00
committed by Matthias Urhahn
parent fead8381bc
commit b6dcc74383
3 changed files with 49 additions and 21 deletions
@@ -183,18 +183,23 @@ data class PodDevice(
).minOrNull()
}
// Charging — AAP preferred, BLE fallback, then cached
// Charging — AAP preferred, BLE fallback, then cached. The cached fallback only applies while
// the device is live (connected): charging is a real-time claim ("power is flowing now") that
// we can't stand behind once the device is out of range, so we suppress it after full
// disconnect rather than showing a frozen charging state on the dimmed "cached N ago" card.
// The raw charging bits are still persisted in `cached` for callers that want the last-known
// value directly (device.cached?.is…Charging).
val isLeftPodCharging: Boolean?
get() = aap?.isLeftCharging ?: (ble as? HasChargeDetectionDual)?.isLeftPodCharging ?: cached?.isLeftCharging
get() = aap?.isLeftCharging ?: (ble as? HasChargeDetectionDual)?.isLeftPodCharging ?: cached?.isLeftCharging?.takeIf { isLive }
val isRightPodCharging: Boolean?
get() = aap?.isRightCharging ?: (ble as? HasChargeDetectionDual)?.isRightPodCharging ?: cached?.isRightCharging
get() = aap?.isRightCharging ?: (ble as? HasChargeDetectionDual)?.isRightPodCharging ?: cached?.isRightCharging?.takeIf { isLive }
val isCaseCharging: Boolean?
get() = aap?.isCaseCharging ?: (ble as? HasCase)?.isCaseCharging ?: cached?.isCaseCharging
get() = aap?.isCaseCharging ?: (ble as? HasCase)?.isCaseCharging ?: cached?.isCaseCharging?.takeIf { isLive }
val isHeadsetBeingCharged: Boolean?
get() = aap?.isHeadsetCharging ?: (ble as? HasChargeDetection)?.isHeadsetBeingCharged ?: cached?.isHeadsetCharging
get() = aap?.isHeadsetCharging ?: (ble as? HasChargeDetection)?.isHeadsetBeingCharged ?: cached?.isHeadsetCharging?.takeIf { isLive }
// Full per-slot charging state — AAP only (BLE + cache don't carry CHARGING_OPTIMIZED).
// Null means "no live AAP reading", which lets callers avoid showing a stale Optimized chip
@@ -71,12 +71,10 @@ class MonitorNotificationViewFactory @Inject constructor(
if (device.hasEarDetection) {
setViewVisibility(R.id.headphones_worn, if (device.isBeingWorn == true) View.VISIBLE else View.GONE)
}
if (device.isHeadsetBeingCharged != null) {
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
private fun createUnknownDevice(device: PodDevice): RemoteViews = RemoteViews(
@@ -151,12 +149,10 @@ class MonitorNotificationViewFactory @Inject constructor(
if (device.hasEarDetection) {
setViewVisibility(R.id.headphones_worn, if (device.isBeingWorn == true) View.VISIBLE else View.GONE)
}
if (device.isHeadsetBeingCharged != null) {
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
setViewVisibility(
R.id.headphones_charging,
if (device.isHeadsetBeingCharged == true) View.VISIBLE else View.GONE
)
}
private fun createUnknownDeviceBig(device: PodDevice): RemoteViews = RemoteViews(
@@ -91,10 +91,37 @@ class PodDeviceCacheTest : BaseTest() {
}
@Test
fun `charging falls back to cache`() {
val device = PodDevice(profileId = "test-profile", ble = null, aap = null, cached = cachedState)
device.isLeftPodCharging shouldBe false
device.isCaseCharging shouldBe true
fun `charging is suppressed when not live even if cached`() {
// Charging is a real-time claim we can't stand behind once the device is out of range.
// A cached-only (non-live) device must report null for every charging slot, so the
// dimmed "cached N ago" card shows no charging chip. Uses a cache with all four slots
// populated to prove each getter suppresses independently.
val fullChargingCache = cachedState.copy(
isLeftCharging = true,
isRightCharging = true,
isCaseCharging = true,
isHeadsetCharging = true,
)
val device = PodDevice(profileId = "test-profile", ble = null, aap = null, cached = fullChargingCache)
device.isLive shouldBe false
device.isLeftPodCharging.shouldBeNull()
device.isRightPodCharging.shouldBeNull()
device.isCaseCharging.shouldBeNull()
device.isHeadsetBeingCharged.shouldBeNull()
}
@Test
fun `charging still uses cache fallback while live`() {
// While a device is connected (here: an AAP session that is READY but hasn't sent a
// per-slot charging reading yet) the cached charging value is still shown, since the
// device is present and a fresh reading will correct it momentarily.
val aap = AapPodState(
connectionState = AapPodState.ConnectionState.READY,
lastMessageAt = fiveMinAgo,
)
val device = PodDevice(profileId = "test-profile", ble = null, aap = aap, cached = cachedState)
device.isLive shouldBe true
device.isCaseCharging shouldBe true // AAP has no case charging -> cache fallback (live)
}
@Test