playback: make the song tap shuffle behaviour a preference

This commit is contained in:
austinried
2026-11-12 07:05:20 +00:00
parent d62bff5ace
commit e94b365495
13 changed files with 231 additions and 11 deletions
@@ -26,6 +26,7 @@ import com.subtracks.data.model.Song
import com.subtracks.data.model.SongDownload
import com.subtracks.data.model.Source
import com.subtracks.data.net.NetworkMode
import com.subtracks.data.prefs.SongTapBehavior
import com.subtracks.data.prefs.StreamQuality
import com.subtracks.data.prefs.UserPreferences
import com.subtracks.data.prefs.fakeUserPreferences
@@ -113,6 +114,7 @@ class PlaybackControllerTest {
queues,
FakePlayerConnection(handle),
downloads,
prefs,
showMessage = { messages += it.resolve(resources) },
scope = controllerScope,
)
@@ -2519,6 +2521,60 @@ class PlaybackControllerTest {
assertEquals("s4", runBlocking { queues.itemAt(snapshot, controller.state.value.position!!)?.song?.id })
}
@Test
fun tappingASongCanKeepTheCurrentShuffleMode() {
seedAlbum(5, sourceId = 1)
runBlocking { prefs.setSongTapBehavior(SongTapBehavior.KeepShuffle) }
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
controller.toggleShuffle()
await { controller.state.value.shuffle }
controller.playAlbum(1, "al1", 3)
await {
controller.state.value.item
?.id == "s4" && controller.state.value.shuffle
}
assertEquals(0L, controller.state.value.position)
val snapshot = runBlocking { queues.snapshot() }
assertTrue(snapshot.shuffled)
assertEquals("s4", runBlocking { queues.itemAt(snapshot, controller.state.value.position!!)?.song?.id })
}
@Test
fun tappingASongCanStartShuffled() {
seedAlbum(5, sourceId = 1)
runBlocking { prefs.setSongTapBehavior(SongTapBehavior.Shuffle) }
controller.playAlbum(1, "al1", 3)
await {
controller.state.value.item
?.id == "s4" && controller.state.value.shuffle
}
assertEquals(0L, controller.state.value.position)
assertTrue(runBlocking { queues.snapshot().shuffled })
}
@Test
fun playAllStartsInOrderRegardlessOfTheSongTapBehavior() {
seedAlbum(3, sourceId = 1)
runBlocking { prefs.setSongTapBehavior(SongTapBehavior.Shuffle) }
controller.playAlbumInOrder(1, "al1")
await {
controller.state.value.item
?.id == "s1" && !controller.state.value.shuffle
}
assertEquals(0L, controller.state.value.position)
}
@Test
fun jumpingInTheQueueWhileShuffledKeepsTheOrder() {
seedAlbum(5, sourceId = 1)
@@ -3643,7 +3699,7 @@ class PlaybackControllerTest {
handle: FakePlayerHandle,
): PlaybackController {
val scope = CoroutineScope(SupervisorJob() + dispatcher).also { extraScopes += it }
return PlaybackController(sources, queues, FakePlayerConnection(handle), downloads, scope = scope)
return PlaybackController(sources, queues, FakePlayerConnection(handle), downloads, prefs, scope = scope)
}
private fun await(predicate: () -> Boolean) = awaitUntil("waiting for the expected playback state", predicate)
@@ -59,6 +59,7 @@ class ViewModelRepositoryFixture {
queueRepository,
FakePlayerConnection(FakePlayerHandle()),
downloadRepository,
fakeUserPreferences(),
scope = controllerScope,
)
val libraryRepository =
@@ -101,6 +101,7 @@ class NowPlayingRouteTest {
queues,
FakePlayerConnection(handle),
downloads,
prefs,
showMessage = {},
scope = controllerScope,
)
@@ -83,7 +83,15 @@ class QueueViewModelTest {
artworkFetcher = { ByteArray(0) },
scope = repoScope,
).also { it.start() }
controller = PlaybackController(sources, queues, FakePlayerConnection(FakePlayerHandle()), downloads, scope = controllerScope)
controller =
PlaybackController(
sources,
queues,
FakePlayerConnection(FakePlayerHandle()),
downloads,
fakeUserPreferences(),
scope = controllerScope,
)
viewModel = QueueViewModel(queues, controller, sources, downloads)
}
@@ -12,6 +12,7 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToNode
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.subtracks.data.prefs.SongTapBehavior
import com.subtracks.data.prefs.SortNameMode
import com.subtracks.data.prefs.StreamQuality
import com.subtracks.ui.theme.SubtracksTheme
@@ -82,10 +83,29 @@ class SettingsScreenTest {
assertEquals(SortNameMode.SortName, requested)
}
@Test
fun theSongTapBehaviorRowChangesTheSetting() {
var requested: SongTapBehavior? = null
renderSettings(
keepScreenOn = false,
onKeepScreenOnChange = {},
onSongTapBehaviorChange = { requested = it },
scrollTo = "New song tap behavior",
)
composeRule.onNodeWithText("New song tap behavior").performClick()
composeRule.onNodeWithText("Play and shuffle").performClick()
composeRule.onNodeWithText("Done").performClick()
composeRule.waitForIdle()
assertEquals(SongTapBehavior.Shuffle, requested)
}
private fun renderSettings(
keepScreenOn: Boolean,
onKeepScreenOnChange: (Boolean) -> Unit,
onSortNameModeChange: (SortNameMode) -> Unit = {},
onSongTapBehaviorChange: (SongTapBehavior) -> Unit = {},
scrollTo: String = "Keep screen on",
) {
composeRule.setContent {
@@ -110,6 +130,7 @@ class SettingsScreenTest {
onScrobblingChange = {},
onKeepScreenOnChange = onKeepScreenOnChange,
onSortNameModeChange = onSortNameModeChange,
onSongTapBehaviorChange = onSongTapBehaviorChange,
onAddServer = {},
onOpenDownloads = {},
onOpenLicenses = {},