Skip to content

Commit ac3bd25

Browse files
committed
Add episode screen
1 parent 6f2e501 commit ac3bd25

File tree

17 files changed

+811
-367
lines changed

17 files changed

+811
-367
lines changed

Jetcaster/core/src/main/java/com/example/jetcaster/core/player/EpisodePlayer.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ interface EpisodePlayer {
4545

4646
fun addToQueue(episode: PlayerEpisode)
4747

48+
/*
49+
* Flushes the queue
50+
*/
51+
fun removeAllFromQueue()
52+
4853
/**
4954
* Plays the current episode
5055
*/
@@ -55,6 +60,11 @@ interface EpisodePlayer {
5560
*/
5661
fun play(playerEpisode: PlayerEpisode)
5762

63+
/**
64+
* Plays the specified list of episodes
65+
*/
66+
fun play(playerEpisodes: List<PlayerEpisode>)
67+
5868
/**
5969
* Pauses the currently played episode
6070
*/

Jetcaster/core/src/main/java/com/example/jetcaster/core/player/MockEpisodePlayer.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class MockEpisodePlayer(
7878
}
7979
}
8080

81+
override fun removeAllFromQueue() {
82+
queue.value = emptyList()
83+
}
84+
8185
override fun play() {
8286
// Do nothing if already playing
8387
if (isPlaying.value) {
@@ -129,6 +133,35 @@ class MockEpisodePlayer(
129133
next()
130134
}
131135

136+
override fun play(playerEpisodes: List<PlayerEpisode>) {
137+
if (isPlaying.value) {
138+
pause()
139+
}
140+
141+
// Keep the currently playing episode in the queue
142+
val playingEpisode = _currentEpisode.value
143+
var previousList: List<PlayerEpisode> = emptyList()
144+
queue.update { queue ->
145+
playerEpisodes.map { episode ->
146+
if (queue.contains(episode)) {
147+
val mutableList = queue.toMutableList()
148+
mutableList.remove(episode)
149+
previousList = mutableList
150+
} else {
151+
previousList = queue
152+
}
153+
}
154+
if (playingEpisode != null) {
155+
playerEpisodes + listOf(playingEpisode) + previousList
156+
} else {
157+
playerEpisodes + previousList
158+
}
159+
}
160+
161+
next()
162+
play()
163+
}
164+
132165
override fun pause() {
133166
isPlaying.value = false
134167

Jetcaster/core/src/test/kotlin/com/example/jetcaster/core/player/MockEpisodePlayerTest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class MockEpisodePlayerTest {
7070
uri = "currentEpisode",
7171
duration = duration
7272
)
73+
7374
mockEpisodePlayer.currentEpisode = currEpisode
7475
testEpisodes.forEach { mockEpisodePlayer.addToQueue(it) }
7576

@@ -78,6 +79,35 @@ class MockEpisodePlayerTest {
7879

7980
assertTrue(mockEpisodePlayer.playerState.value.isPlaying)
8081
}
82+
@Test
83+
fun whenPlayListofEpisodes_playerAutoPlaysNextEpisode() = runTest(testDispatcher) {
84+
val duration = Duration.ofSeconds(60)
85+
val currEpisode = PlayerEpisode(
86+
uri = "currentEpisode",
87+
duration = duration
88+
)
89+
val firstEpisodeFromList = PlayerEpisode(
90+
uri = "firstEpisodeFromList",
91+
duration = duration
92+
)
93+
val secondEpisodeFromList = PlayerEpisode(
94+
uri = "secondEpisodeFromList",
95+
duration = duration
96+
)
97+
val episodeListToBeAddedToTheQueue: List<PlayerEpisode> = listOf(
98+
firstEpisodeFromList, secondEpisodeFromList
99+
)
100+
mockEpisodePlayer.currentEpisode = currEpisode
101+
102+
mockEpisodePlayer.play(episodeListToBeAddedToTheQueue)
103+
assertEquals(firstEpisodeFromList, mockEpisodePlayer.currentEpisode)
104+
105+
advanceTimeBy(duration.toMillis() + 1)
106+
assertEquals(secondEpisodeFromList, mockEpisodePlayer.currentEpisode)
107+
108+
advanceTimeBy(duration.toMillis() + 1)
109+
assertEquals(currEpisode, mockEpisodePlayer.currentEpisode)
110+
}
81111

82112
@Test
83113
fun whenNext_queueIsEmpty_doesNothing() {

Jetcaster/wear/src/main/java/com/example/jetcaster/WearApp.kt

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import androidx.wear.compose.navigation.composable
2525
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
2626
import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
2727
import com.example.jetcaster.theme.WearAppTheme
28+
import com.example.jetcaster.ui.Episode
29+
import com.example.jetcaster.ui.JetcasterNavController.navigateToEpisode
2830
import com.example.jetcaster.ui.JetcasterNavController.navigateToLatestEpisode
2931
import com.example.jetcaster.ui.JetcasterNavController.navigateToPodcastDetails
3032
import com.example.jetcaster.ui.JetcasterNavController.navigateToUpNext
@@ -33,6 +35,7 @@ import com.example.jetcaster.ui.LatestEpisodes
3335
import com.example.jetcaster.ui.PodcastDetails
3436
import com.example.jetcaster.ui.UpNext
3537
import com.example.jetcaster.ui.YourPodcasts
38+
import com.example.jetcaster.ui.episode.EpisodeScreen
3639
import com.example.jetcaster.ui.home.HomeScreen
3740
import com.example.jetcaster.ui.library.LatestEpisodesScreen
3841
import com.example.jetcaster.ui.library.PodcastsScreen
@@ -90,39 +93,45 @@ fun WearApp() {
9093
) {
9194
LatestEpisodesScreen(
9295
playlistName = stringResource(id = R.string.latest_episodes),
93-
// TODO implement change speed
94-
onChangeSpeedButtonClick = {},
9596
onPlayButtonClick = {
9697
navController.navigateToPlayer()
97-
}
98+
},
99+
onDismiss = { navController.popBackStack() }
98100
)
99101
}
100102
composable(route = YourPodcasts.navRoute) {
101103
PodcastsScreen(
102104
onPodcastsItemClick = { navController.navigateToPodcastDetails(it.uri) },
103-
onErrorDialogCancelClick = { navController.popBackStack() }
105+
onDismiss = { navController.popBackStack() }
104106
)
105107
}
106108
composable(route = PodcastDetails.navRoute) {
107109
PodcastDetailsScreen(
108-
// TODO implement change speed
109-
onChangeSpeedButtonClick = {},
110110
onPlayButtonClick = {
111111
navController.navigateToPlayer()
112112
},
113-
onEpisodeItemClick = { navController.navigateToPlayer() },
114-
onErrorDialogCancelClick = { navController.popBackStack() }
113+
onEpisodeItemClick = { navController.navigateToEpisode(it.uri) },
114+
onDismiss = { navController.popBackStack() }
115115
)
116116
}
117117
composable(route = UpNext.navRoute) {
118118
QueueScreen(
119-
// TODO implement change speed
120-
onChangeSpeedButtonClick = {},
121119
onPlayButtonClick = {
122120
navController.navigateToPlayer()
123121
},
124122
onEpisodeItemClick = { navController.navigateToPlayer() },
125-
onErrorDialogCancelClick = {
123+
onDismiss = {
124+
navController.popBackStack()
125+
navController.navigateToYourPodcast()
126+
}
127+
)
128+
}
129+
composable(route = Episode.navRoute) {
130+
EpisodeScreen(
131+
onPlayButtonClick = {
132+
navController.navigateToPlayer()
133+
},
134+
onDismiss = {
126135
navController.popBackStack()
127136
navController.navigateToYourPodcast()
128137
}

Jetcaster/wear/src/main/java/com/example/jetcaster/ui/JetcasterNavController.kt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public object JetcasterNavController {
4343
public fun NavController.navigateToUpNext() {
4444
navigate(UpNext.destination())
4545
}
46+
47+
public fun NavController.navigateToEpisode(episodeUri: String) {
48+
navigate(Episode.destination(episodeUri))
49+
}
4650
}
4751

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

5660
public object PodcastDetails : NavigationScreens("podcast?podcastUri={podcastUri}") {
57-
public const val podcastUri: String = "podcastUri"
58-
public fun destination(podcastUriValue: String): String {
59-
val encodedUri = Uri.encode(podcastUriValue)
60-
return "podcast?$podcastUri=$encodedUri"
61+
public const val PODCAST_URI: String = "podcastUri"
62+
public fun destination(podcastUri: String): String {
63+
val encodedUri = Uri.encode(podcastUri)
64+
return "podcast?$PODCAST_URI=$encodedUri"
65+
}
66+
67+
override val arguments: List<NamedNavArgument>
68+
get() = listOf(
69+
navArgument(PODCAST_URI) {
70+
type = NavType.StringType
71+
},
72+
)
73+
}
74+
75+
public object Episode : NavigationScreens("episode?episodeUri={episodeUri}") {
76+
public const val EPISODE_URI: String = "episodeUri"
77+
public fun destination(episodeUri: String): String {
78+
val encodedUri = Uri.encode(episodeUri)
79+
return "episode?$EPISODE_URI=$encodedUri"
6180
}
6281

6382
override val arguments: List<NamedNavArgument>
6483
get() = listOf(
65-
navArgument(podcastUri) {
84+
navArgument(EPISODE_URI) {
6685
type = NavType.StringType
6786
},
6887
)

Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/LoadingEntityScreen.kt

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)