Fix auto-resume after CAP pause

This commit is contained in:
darken
2026-04-14 11:28:41 +02:00
committed by Matthias Urhahn
parent b8f342f096
commit c1601c105c
5 changed files with 155 additions and 13 deletions
@@ -0,0 +1,68 @@
package eu.darken.capod.common
import android.media.AudioManager
import android.os.SystemClock
import io.mockk.Runs
import io.mockk.clearMocks
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import io.mockk.verify
import org.junit.jupiter.api.AfterEach
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
class MediaControlTest : BaseTest() {
private lateinit var audioManager: AudioManager
private lateinit var mediaControl: MediaControl
@BeforeEach
fun setup() {
mockkStatic(SystemClock::class)
every { SystemClock.elapsedRealtime() } returns 1_000L
every { SystemClock.uptimeMillis() } returns 1_000L
audioManager = mockk(relaxed = true)
every { audioManager.dispatchMediaKeyEvent(any()) } just Runs
mediaControl = MediaControl(audioManager)
}
@AfterEach
fun teardown() {
unmockkStatic(SystemClock::class)
}
@Test
fun `sendPlay ignores stale active state after cap pause`() = runTest {
every { audioManager.isMusicActive } returns true
mediaControl.sendPause()
assertTrue(mediaControl.wasRecentlyPausedByCap)
clearMocks(audioManager, answers = false, recordedCalls = true)
mediaControl.sendPlay()
assertFalse(mediaControl.wasRecentlyPausedByCap)
verify(exactly = 2) { audioManager.dispatchMediaKeyEvent(any()) }
}
@Test
fun `sendPlayPause resumes after cap pause even when audio manager still reports active`() = runTest {
every { audioManager.isMusicActive } returns true
mediaControl.sendPause()
assertTrue(mediaControl.wasRecentlyPausedByCap)
clearMocks(audioManager, answers = false, recordedCalls = true)
mediaControl.sendPlayPause()
assertFalse(mediaControl.wasRecentlyPausedByCap)
verify(exactly = 2) { audioManager.dispatchMediaKeyEvent(any()) }
}
}
@@ -16,6 +16,8 @@ import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkObject
import org.junit.jupiter.api.AfterEach
import kotlinx.coroutines.flow.flowOf
import org.junit.jupiter.api.BeforeEach
import testhelpers.BaseTest
@@ -62,6 +64,11 @@ abstract class BaseBlePodsTest : BaseTest() {
every { SystemClockWrap.elapsedRealtimeNanos } returns 1000L
}
@AfterEach
fun teardown() {
unmockkObject(SystemClockWrap)
}
internal suspend inline fun <reified T : BlePodSnapshot?> create(
hex: String,
address: String = "77:49:4C:D8:25:0C",
@@ -79,4 +86,4 @@ abstract class BaseBlePodsTest : BaseTest() {
block.invoke(factory.create(result) as T)
}
}
}
@@ -89,6 +89,24 @@ class PlayPauseLogicTest : BaseTest() {
decision.shouldPause shouldBe false
}
@Test
fun `one in to both in - should play if recently paused by cap`() {
val previous = EarDetectionState.fromDualPod(left = true, right = false)
val current = EarDetectionState.fromDualPod(left = true, right = true)
val decision = playPause.evaluatePlayPauseAction(
previous = previous,
current = current,
onePodMode = false,
isCurrentlyPlaying = true,
wasRecentlyPausedByUs = true,
)
decision.shouldPlay shouldBe true
decision.shouldPause shouldBe false
decision.usedRecentCapPauseOverride shouldBe true
}
@Test
fun `none in to one in - no action (need both)`() {
val previous = EarDetectionState.fromDualPod(left = false, right = false)
@@ -288,6 +306,24 @@ class PlayPauseLogicTest : BaseTest() {
decision.shouldPause shouldBe false
}
@Test
fun `one in to both in - should play if recently paused by cap`() {
val previous = EarDetectionState.fromDualPod(left = true, right = false)
val current = EarDetectionState.fromDualPod(left = true, right = true)
val decision = playPause.evaluatePlayPauseAction(
previous = previous,
current = current,
onePodMode = true,
isCurrentlyPlaying = true,
wasRecentlyPausedByUs = true,
)
decision.shouldPlay shouldBe true
decision.shouldPause shouldBe false
decision.usedRecentCapPauseOverride shouldBe true
}
@Test
fun `both in to one in - should pause if playing`() {
val previous = EarDetectionState.fromDualPod(left = true, right = true)