diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapSetting.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapSetting.kt index ad1e9287..d24a9ad4 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapSetting.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapSetting.kt @@ -103,9 +103,10 @@ sealed class AapSetting { * Push-only from device — reports speaking detection state (command 0x4B). * * [rawValue] is the status byte: the last byte of the 4-byte `02 00 01 XX` form (or the single - * byte of the legacy form), preserved so consumers can classify it. [speaking] is `true` only - * for the speaking-onset statuses (`1`, `2`); every other value (`0`, `3`, `4`, `5`, `0x0B`, …) - * is `false`. START/RESUME/HOLD/STOP classification for the reaction lives in [ConversationAwarenessEvent]. + * byte of the legacy form), preserved so consumers can classify it. [speaking] is `true` while + * the wearer is talking — onset (`1`, `2`) or resumed after a pause (`5`); every other value + * (`0`, `3`, `4`, `0x0B`, …) is `false`. START/RESUME/HOLD/STOP classification for the reaction + * lives in [ConversationAwarenessEvent]. */ data class ConversationalAwarenessState( val speaking: Boolean, diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt index dcd503a3..0a547229 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt @@ -197,8 +197,11 @@ class DefaultAapDeviceProfile( p[3].toInt() and 0xFF else -> return null } - // speaking = the "started/active speaking" statuses (1,2); see ConversationAwarenessEvent. - val speaking = status in ConversationAwarenessEvent.SPEAKING_STATUSES + // speaking = the wearer is currently speaking: onset (1,2) or resumed after a pause (5). + // See ConversationAwarenessEvent. (Consumers read rawValue for full classification; this + // bool just exposes "is talking now", so a resume must count as speaking too.) + val speaking = status in ConversationAwarenessEvent.SPEAKING_STATUSES || + status in ConversationAwarenessEvent.RESUME_STATUSES return AapSetting.ConversationalAwarenessState::class to AapSetting.ConversationalAwarenessState(speaking, rawValue = status) } diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt index c70fa2f8..7d302779 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt @@ -412,6 +412,8 @@ class DefaultAapDeviceProfileTest : BaseAapSessionTest() { @Nested inner class ConversationAwarenessStateTests { @Test fun `speaking start`() { decodeSetting(aapMessage("04 00 04 00 4B 00 01")).speaking shouldBe true } + @Test fun `speaking resume`() { decodeSetting(aapMessage("04 00 04 00 4B 00 05")).speaking shouldBe true } + @Test fun `speaking pause`() { decodeSetting(aapMessage("04 00 04 00 4B 00 03")).speaking shouldBe false } @Test fun `speaking stop`() { decodeSetting(aapMessage("04 00 04 00 4B 00 04")).speaking shouldBe false } @Test fun `empty payload returns null`() { profile.decodeSetting(aapMessage("04 00 04 00 4B 00")).shouldBeNull() } }