Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ interface EpisodePlayer {

fun addToQueue(episode: PlayerEpisode)

/*
* Flushes the queue
*/
fun removeAllFromQueue()

/**
* Plays the current episode
*/
Expand All @@ -55,6 +60,11 @@ interface EpisodePlayer {
*/
fun play(playerEpisode: PlayerEpisode)

/**
* Plays the specified list of episodes
*/
fun play(playerEpisodes: List<PlayerEpisode>)

/**
* Pauses the currently played episode
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class MockEpisodePlayer(
}
}

override fun removeAllFromQueue() {
queue.value = emptyList()
}

override fun play() {
// Do nothing if already playing
if (isPlaying.value) {
Expand Down Expand Up @@ -105,24 +109,31 @@ class MockEpisodePlayer(
}

override fun play(playerEpisode: PlayerEpisode) {
play(listOf(playerEpisode))
}

override fun play(playerEpisodes: List<PlayerEpisode>) {
if (isPlaying.value) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could...

never mind...

I'm not 100% convinced this is the exact logic I'd expect in a player, but seems fine.

pause()
}

// Keep the currently playing episode in the queue
val playingEpisode = _currentEpisode.value
queue.update {
val previousList = if (it.contains(playerEpisode)) {
val mutableList = it.toMutableList()
mutableList.remove(playerEpisode)
mutableList
} else {
it
var previousList: List<PlayerEpisode> = emptyList()
queue.update { queue ->
playerEpisodes.map { episode ->
if (queue.contains(episode)) {
val mutableList = queue.toMutableList()
mutableList.remove(episode)
previousList = mutableList
} else {
previousList = queue
}
}
if (playingEpisode != null) {
listOf(playerEpisode, playingEpisode) + previousList
playerEpisodes + listOf(playingEpisode) + previousList
} else {
listOf(playerEpisode) + previousList
playerEpisodes + previousList
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class MockEpisodePlayerTest {
uri = "currentEpisode",
duration = duration
)

mockEpisodePlayer.currentEpisode = currEpisode
testEpisodes.forEach { mockEpisodePlayer.addToQueue(it) }

Expand All @@ -78,6 +79,35 @@ class MockEpisodePlayerTest {

assertTrue(mockEpisodePlayer.playerState.value.isPlaying)
}
@Test
fun whenPlayListOfEpisodes_playerAutoPlaysNextEpisode() = runTest(testDispatcher) {
val duration = Duration.ofSeconds(60)
val currEpisode = PlayerEpisode(
uri = "currentEpisode",
duration = duration
)
val firstEpisodeFromList = PlayerEpisode(
uri = "firstEpisodeFromList",
duration = duration
)
val secondEpisodeFromList = PlayerEpisode(
uri = "secondEpisodeFromList",
duration = duration
)
val episodeListToBeAddedToTheQueue: List<PlayerEpisode> = listOf(
firstEpisodeFromList, secondEpisodeFromList
)
mockEpisodePlayer.currentEpisode = currEpisode

mockEpisodePlayer.play(episodeListToBeAddedToTheQueue)
assertEquals(firstEpisodeFromList, mockEpisodePlayer.currentEpisode)

advanceTimeBy(duration.toMillis() + 1)
assertEquals(secondEpisodeFromList, mockEpisodePlayer.currentEpisode)

advanceTimeBy(duration.toMillis() + 1)
assertEquals(currEpisode, mockEpisodePlayer.currentEpisode)
}

@Test
fun whenNext_queueIsEmpty_doesNothing() {
Expand Down
31 changes: 20 additions & 11 deletions Jetcaster/wear/src/main/java/com/example/jetcaster/WearApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import androidx.wear.compose.navigation.composable
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
import com.example.jetcaster.theme.WearAppTheme
import com.example.jetcaster.ui.Episode
import com.example.jetcaster.ui.JetcasterNavController.navigateToEpisode
import com.example.jetcaster.ui.JetcasterNavController.navigateToLatestEpisode
import com.example.jetcaster.ui.JetcasterNavController.navigateToPodcastDetails
import com.example.jetcaster.ui.JetcasterNavController.navigateToUpNext
Expand All @@ -33,6 +35,7 @@ import com.example.jetcaster.ui.LatestEpisodes
import com.example.jetcaster.ui.PodcastDetails
import com.example.jetcaster.ui.UpNext
import com.example.jetcaster.ui.YourPodcasts
import com.example.jetcaster.ui.episode.EpisodeScreen
import com.example.jetcaster.ui.home.HomeScreen
import com.example.jetcaster.ui.library.LatestEpisodesScreen
import com.example.jetcaster.ui.library.PodcastsScreen
Expand Down Expand Up @@ -90,39 +93,45 @@ fun WearApp() {
) {
LatestEpisodesScreen(
playlistName = stringResource(id = R.string.latest_episodes),
// TODO implement change speed
onChangeSpeedButtonClick = {},
onPlayButtonClick = {
navController.navigateToPlayer()
}
},
onDismiss = { navController.popBackStack() }
)
}
composable(route = YourPodcasts.navRoute) {
PodcastsScreen(
onPodcastsItemClick = { navController.navigateToPodcastDetails(it.uri) },
onErrorDialogCancelClick = { navController.popBackStack() }
onDismiss = { navController.popBackStack() }
)
}
composable(route = PodcastDetails.navRoute) {
PodcastDetailsScreen(
// TODO implement change speed
onChangeSpeedButtonClick = {},
onPlayButtonClick = {
navController.navigateToPlayer()
},
onEpisodeItemClick = { navController.navigateToPlayer() },
onErrorDialogCancelClick = { navController.popBackStack() }
onEpisodeItemClick = { navController.navigateToEpisode(it.uri) },
onDismiss = { navController.popBackStack() }
)
}
composable(route = UpNext.navRoute) {
QueueScreen(
// TODO implement change speed
onChangeSpeedButtonClick = {},
onPlayButtonClick = {
navController.navigateToPlayer()
},
onEpisodeItemClick = { navController.navigateToPlayer() },
onErrorDialogCancelClick = {
onDismiss = {
navController.popBackStack()
navController.navigateToYourPodcast()
}
)
}
composable(route = Episode.navRoute) {
EpisodeScreen(
onPlayButtonClick = {
navController.navigateToPlayer()
},
onDismiss = {
navController.popBackStack()
navController.navigateToYourPodcast()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public object JetcasterNavController {
public fun NavController.navigateToUpNext() {
navigate(UpNext.destination())
}

public fun NavController.navigateToEpisode(episodeUri: String) {
navigate(Episode.destination(episodeUri))
}
}

public object YourPodcasts : NavigationScreens("yourPodcasts") {
Expand All @@ -54,15 +58,30 @@ public object LatestEpisodes : NavigationScreens("latestEpisodes") {
}

public object PodcastDetails : NavigationScreens("podcast?podcastUri={podcastUri}") {
public const val podcastUri: String = "podcastUri"
public fun destination(podcastUriValue: String): String {
val encodedUri = Uri.encode(podcastUriValue)
return "podcast?$podcastUri=$encodedUri"
public const val PODCAST_URI: String = "podcastUri"
public fun destination(podcastUri: String): String {
val encodedUri = Uri.encode(podcastUri)
return "podcast?$PODCAST_URI=$encodedUri"
}

override val arguments: List<NamedNavArgument>
get() = listOf(
navArgument(PODCAST_URI) {
type = NavType.StringType
},
)
}

public object Episode : NavigationScreens("episode?episodeUri={episodeUri}") {
public const val EPISODE_URI: String = "episodeUri"
public fun destination(episodeUri: String): String {
val encodedUri = Uri.encode(episodeUri)
return "episode?$EPISODE_URI=$encodedUri"
}

override val arguments: List<NamedNavArgument>
get() = listOf(
navArgument(podcastUri) {
navArgument(EPISODE_URI) {
type = NavType.StringType
},
)
Expand Down

This file was deleted.

Loading