move seasons and episode list to separate screen

This commit is contained in:
Owen LeJeune
2023-06-27 17:17:01 -04:00
parent 3d3711cee9
commit f9aa5357dc
10 changed files with 435 additions and 280 deletions

View File

@@ -143,6 +143,9 @@ dependencies {
def compose_markdown = "0.3.3"
implementation "com.github.jeziellago:compose-markdown:$compose_markdown"
def cloudy = "0.1.2"
implementation "com.github.skydoves:cloudy:$cloudy"
// testing
def junit = "4.13.2"
def androidx_junit = "1.1.3"

View File

@@ -1,18 +1,21 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import java.util.Date
data class Episode(
@SerializedName("name")
val name: String,
@SerializedName("overview")
val overview: String,
@SerializedName("episode_number")
val episodeNumber: Int,
@SerializedName("season_number")
val seasonNumber: Int,
@SerializedName("still_path")
val stillPath: String?,
@SerializedName("air_date")
val airDate: String?
@SerializedName("air_date") val airDate: Date?,
@SerializedName("episode_number") val episodeNumber: Int,
@SerializedName("id") val id: Int,
@SerializedName("name") val name: String,
@SerializedName("overview") val overview: String,
@SerializedName("production_code") val productionCode: String,
@SerializedName("runtime") val runtime: Int,
@SerializedName("season_number") val seasonNumber: Int,
@SerializedName("show_id") val showId: Int,
@SerializedName("still_path") val stillPath: String?,
@SerializedName("vote_average") val voteAverage: Float,
@SerializedName("vote_count") val voteCount: Int,
@SerializedName("crew") val crew: List<CrewMember>?,
@SerializedName("guest_starts") val guestStars: List<CastMember>?
)

View File

@@ -1,10 +1,15 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import java.util.Date
data class Season(
@SerializedName("name")
val name: String,
@SerializedName("episodes")
val episodes: List<Episode>
@SerializedName("_id") val _id: String,
@SerializedName("air_date") val airDate: Date?,
@SerializedName("episodes") val episodes: List<Episode>,
@SerializedName("name") val name: String,
@SerializedName("overview") val overview: String,
@SerializedName("id") val id: Int,
@SerializedName("poster_path") val posterPath: String?,
@SerializedName("season_number") val seasonNumber: Int
)

View File

@@ -19,3 +19,7 @@ fun <T> List<T>.bringToFront(predicate: (T) -> Boolean): List<T> {
}
return frontItems.plus(orig)
}
fun <T> List<T>.subListLimit(limit: Int, fromIndex: Int = 0): List<T> {
return subList(fromIndex = fromIndex, toIndex = minOf(size, fromIndex+limit))
}

View File

@@ -28,6 +28,7 @@ import com.owenlejeune.tvtime.ui.screens.ListDetailScreen
import com.owenlejeune.tvtime.ui.screens.MediaDetailScreen
import com.owenlejeune.tvtime.ui.screens.PersonDetailScreen
import com.owenlejeune.tvtime.ui.screens.SearchScreen
import com.owenlejeune.tvtime.ui.screens.SeasonListScreen
import com.owenlejeune.tvtime.ui.screens.SettingsScreen
import com.owenlejeune.tvtime.ui.screens.WebLinkScreen
import com.owenlejeune.tvtime.utils.NavConstants
@@ -200,6 +201,16 @@ fun AppNavigationHost(
GalleryView(id = id, type = type, appNavController = appNavController)
}
composable(
route = AppNavItem.SeasonListView.route.plus("/{${NavConstants.ID_KEY}}"),
arguments = listOf(
navArgument(NavConstants.ID_KEY) { type = NavType.IntType }
)
) { navBackStackEntry ->
val id = navBackStackEntry.arguments?.getInt(NavConstants.ID_KEY)!!
SeasonListScreen(id = id, appNavController = appNavController)
}
}
}
@@ -233,5 +244,8 @@ sealed class AppNavItem(val route: String) {
object GalleryView: AppNavItem("gallery_view_route") {
fun withArgs(type: MediaViewType, id: Int) = route.plus("/$type/$id")
}
object SeasonListView: AppNavItem("season_list_route") {
fun withArgs(id: Int) = route.plus("/$id")
}
}

View File

@@ -4,8 +4,6 @@ import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.compose.animation.*
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
@@ -14,16 +12,13 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Movie
import androidx.compose.material.icons.filled.Send
import androidx.compose.material.icons.outlined.ExpandMore
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
@@ -38,13 +33,13 @@ import androidx.paging.compose.collectAsLazyPagingItems
import coil.compose.AsyncImage
import com.google.accompanist.flowlayout.FlowRow
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.rememberPagerState
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
import com.owenlejeune.tvtime.extensions.WindowSizeClass
import com.owenlejeune.tvtime.extensions.getCalendarYear
import com.owenlejeune.tvtime.extensions.lazyPagingItems
import com.owenlejeune.tvtime.extensions.listItems
import com.owenlejeune.tvtime.ui.components.*
@@ -53,7 +48,6 @@ import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
import com.owenlejeune.tvtime.utils.SessionManager
import com.owenlejeune.tvtime.utils.TmdbUtils
import com.owenlejeune.tvtime.utils.types.MediaViewType
import com.owenlejeune.tvtime.utils.types.TabNavItem
import kotlinx.coroutines.*
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
@@ -204,24 +198,55 @@ private fun MediaViewContent(
ActionsView(itemId = itemId, type = type, modifier = Modifier.padding(start = 20.dp))
}
if (type == MediaViewType.MOVIE) {
MainContentMovie(
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(horizontal = 16.dp)
) {
OverviewCard(
itemId = itemId,
mediaItem = mediaItem,
type = type,
appNavController = appNavController,
windowSize = windowSize,
mainViewModel = mainViewModel
mainViewModel = mainViewModel,
appNavController = appNavController
)
} else {
MainContentTv(
CastCard(
itemId = itemId,
mediaItem = mediaItem,
type = type,
appNavController = appNavController,
windowSize = windowSize,
type = type,
mainViewModel = mainViewModel
)
if (type == MediaViewType.TV) {
SeasonCard(
itemId = itemId,
mediaItem = mediaItem,
mainViewModel = mainViewModel,
appNavController = appNavController
)
}
SimilarContentCard(
itemId = itemId,
mediaType = type,
appNavController = appNavController,
mainViewModel = mainViewModel
)
VideosCard(
itemId = itemId,
modifier = Modifier.fillMaxWidth(),
mainViewModel = mainViewModel,
type = type
)
AdditionalDetailsCard(mediaItem = mediaItem, type = type)
WatchProvidersCard(itemId = itemId, type = type, mainViewModel = mainViewModel)
if (windowSize != WindowSizeClass.Expanded) {
ReviewsCard(itemId = itemId, type = type, mainViewModel = mainViewModel)
}
}
}
@@ -243,165 +268,6 @@ private fun MediaViewContent(
}
}
@OptIn(ExperimentalPagerApi::class)
@Composable
private fun TvSeriesTabs(
pagerState: PagerState,
tabs: List<TabNavItem>
) {
Tabs(
modifier = Modifier.offset(y = (-16).dp),
pagerState = pagerState,
tabs = tabs
)
}
@Composable
private fun SeasonsTab(
itemId: Int,
mediaItem: DetailedItem?,
mainViewModel: MainViewModel
) {
LaunchedEffect(mediaItem) {
val numSeasons = (mediaItem as DetailedTv?)?.numberOfSeasons ?: 0
for (i in 0..numSeasons) {
mainViewModel.getSeason(itemId, i)
}
}
val seasonsMap = remember { mainViewModel.tvSeasons }
val seasons = seasonsMap[itemId]
seasons?.forEach { season ->
SeasonSection(season = season)
}
}
@Composable
private fun SeasonSection(season: Season) {
var isExpanded by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.clip(RoundedCornerShape(10.dp))
.background(color = MaterialTheme.colorScheme.surfaceVariant)
.clickable {
isExpanded = !isExpanded
}
) {
Row(
modifier = Modifier.padding(horizontal = 24.dp, vertical = 24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = season.name,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.weight(1f))
var currentRotation by remember { mutableStateOf(0f) }
val rotation = remember { androidx.compose.animation.core.Animatable(currentRotation) }
LaunchedEffect(isExpanded) {
rotation.animateTo(
targetValue = if (isExpanded) 180f else 0f,
animationSpec = tween(200, easing = LinearEasing)
) {
currentRotation = value
}
}
Icon(
imageVector = Icons.Outlined.ExpandMore,
contentDescription = null,
modifier = Modifier
.size(32.dp)
.rotate(currentRotation),
tint = MaterialTheme.colorScheme.onSurface,
)
}
}
AnimatedVisibility(
visible = isExpanded,
enter = expandVertically(),
exit = shrinkVertically()
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
season.episodes.forEachIndexed { index, episode ->
EpisodeItem(episode = episode)
if (index != season.episodes.size - 1) {
Divider()
}
}
}
}
}
@Composable
private fun EpisodeItem(episode: Episode) {
val height = 170.dp
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.height(height)
) {
AsyncImage(
model = TmdbUtils.getFullEpisodeStillPath(episode),
contentDescription = null,
modifier = Modifier
.size(width = 100.dp, height = height)
.clip(RoundedCornerShape(10.dp)),
contentScale = ContentScale.Crop
)
Column(
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "S${episode.seasonNumber}E${episode.episodeNumber}${episode.name}",
color = MaterialTheme.colorScheme.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
TmdbUtils.convertEpisodeDate(episode.airDate)?.let {
Text(
text = it,
fontStyle = FontStyle.Italic,
fontSize = 12.sp
)
}
Text(
text = episode.overview,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.onBackground
)
}
}
}
@Composable
private fun MainContent(
itemId: Int,
mediaItem: DetailedItem?,
type: MediaViewType,
appNavController: NavController,
windowSize: WindowSizeClass,
mainViewModel: MainViewModel
) {
OverviewCard(itemId = itemId, mediaItem = mediaItem, type = type, mainViewModel = mainViewModel, appNavController = appNavController)
CastCard(itemId = itemId, appNavController = appNavController, type = type, mainViewModel = mainViewModel)
SimilarContentCard(itemId = itemId, mediaType = type, appNavController = appNavController, mainViewModel = mainViewModel)
VideosCard(itemId = itemId, modifier = Modifier.fillMaxWidth(), mainViewModel = mainViewModel, type = type)
AdditionalDetailsCard(mediaItem = mediaItem, type = type)
WatchProvidersCard(itemId = itemId, type = type, mainViewModel = mainViewModel)
if (windowSize != WindowSizeClass.Expanded) {
ReviewsCard(itemId = itemId, type = type, mainViewModel = mainViewModel)
}
}
@Composable
private fun MiscTvDetails(
itemId: Int,
@@ -778,6 +644,77 @@ private fun CastCrewCard(appNavController: NavController, person: Person) {
)
}
@Composable
private fun SeasonCard(
itemId: Int,
mediaItem: DetailedItem?,
mainViewModel: MainViewModel,
appNavController: NavController
) {
LaunchedEffect(mediaItem) {
val lastSeason = (mediaItem as DetailedTv?)?.numberOfSeasons ?: 0
if (lastSeason > 0) {
mainViewModel.getSeason(itemId, lastSeason)
}
}
val seasonsMap = remember { mainViewModel.tvSeasons }
val lastSeason = seasonsMap[itemId]?.lastOrNull()
lastSeason?.let {
ContentCard(
title = "Latest Season"
) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(all = 12.dp)
) {
PosterItem(
url = TmdbUtils.getFullPosterPath(it.posterPath),
title = it.name,
overrideShowTitle = false,
enabled = false
)
Column(
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = it.name,
style = MaterialTheme.typography.titleMedium
)
Text(
text = "${it.airDate?.getCalendarYear()} | ${it.episodes.size} Episodes"
)
Text(
text = it.overview,
fontSize = 14.sp,
overflow = TextOverflow.Ellipsis
)
}
}
Box(
modifier = Modifier
.padding(start = 12.dp, bottom = 12.dp)
.clip(RoundedCornerShape(10.dp))
.clickable {
appNavController.navigate(AppNavItem.SeasonListView.withArgs(id = itemId))
}
) {
Text(
text = "See all seasons",
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 14.sp,
modifier = Modifier.padding(4.dp)
)
}
}
}
}
@Composable
fun SimilarContentCard(
itemId: Int,
@@ -914,7 +851,6 @@ private fun VideoGroup(results: List<Video>, type: Video.Type, title: String) {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun WatchProvidersCard(
itemId: Int,
@@ -945,7 +881,7 @@ private fun WatchProvidersCard(
color = MaterialTheme.colorScheme.onSurfaceVariant
)
var selectedIndex by remember { mutableStateOf(
var selectedIndex by remember { mutableIntStateOf(
when {
providers.flaterate != null -> 0
providers.rent != null -> 1
@@ -1193,82 +1129,3 @@ fun DetailsFor(
)
}
}
@Composable
fun MainContentMovie(
type: MediaViewType,
itemId: Int,
mediaItem: DetailedItem?,
appNavController: NavController,
windowSize: WindowSizeClass,
mainViewModel: MainViewModel
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(horizontal = 16.dp)
) {
MainContent(
itemId = itemId,
mediaItem = mediaItem,
type = type,
appNavController = appNavController,
windowSize = windowSize,
mainViewModel = mainViewModel
)
}
}
@OptIn(ExperimentalPagerApi::class)
@Composable
fun MainContentTv(
type: MediaViewType,
itemId: Int,
mediaItem: DetailedItem?,
mainViewModel: MainViewModel,
appNavController: NavController,
windowSize: WindowSizeClass
) {
val tabState = rememberPagerState()
val tabs = listOf(DetailsTab, SeasonsTab)
TvSeriesTabs(pagerState = tabState, tabs = tabs)
HorizontalPager(
count = tabs.size,
state = tabState,
userScrollEnabled = false
) { page ->
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(horizontal = 16.dp)
) {
when (tabs[page]) {
is DetailsTab -> {
MainContent(
itemId = itemId,
mediaItem = mediaItem,
type = type,
appNavController = appNavController,
windowSize = windowSize,
mainViewModel = mainViewModel
)
}
is SeasonsTab -> {
SeasonsTab(
itemId = itemId,
mediaItem = mediaItem,
mainViewModel = mainViewModel
)
}
}
}
}
}
object DetailsTab: TabNavItem("details_route") {
override val name: String
get() = "Details"
}
object SeasonsTab: TabNavItem("seasons_route") {
override val name: String
get() = "Seasons"
}

View File

@@ -0,0 +1,262 @@
package com.owenlejeune.tvtime.ui.screens
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.ExpandMore
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Episode
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Season
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
import com.owenlejeune.tvtime.utils.TmdbUtils
import com.skydoves.cloudy.Cloudy
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SeasonListScreen(
id: Int,
appNavController: NavController
) {
val mainViewModel = viewModel<MainViewModel>()
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
LaunchedEffect(Unit) {
val numSeasons = mainViewModel.detailedTv[id]!!.numberOfSeasons
for (i in 0..numSeasons) {
mainViewModel.getSeason(id, i)
}
}
val topAppBarScrollState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(topAppBarScrollState)
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
TopAppBar(
scrollBehavior = scrollBehavior,
colors = TopAppBarDefaults
.topAppBarColors(
scrolledContainerColor = MaterialTheme.colorScheme.background,
titleContentColor = MaterialTheme.colorScheme.primary
),
title = { },
navigationIcon = {
IconButton(
onClick = { appNavController.popBackStack() }
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(id = R.string.content_description_back_button),
tint = MaterialTheme.colorScheme.primary
)
}
}
)
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
Column(
verticalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier
.padding(12.dp)
.verticalScroll(state = rememberScrollState())
) {
val seasonsMap = remember { mainViewModel.tvSeasons }
val seasons = seasonsMap[id] ?: emptyList()
seasons.sortedBy { it.seasonNumber }.forEach { season ->
SeasonSection(season = season)
}
}
}
}
}
@Composable
private fun SeasonSection(season: Season) {
var isExpanded by remember { mutableStateOf(false) }
Row(
modifier = Modifier.padding(horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.weight(1f)
.clip(RoundedCornerShape(10.dp))
.clickable {
}
) {
Text(
text = season.name,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier
.align(Alignment.CenterStart)
.padding(vertical = 12.dp, horizontal = 4.dp)
)
}
var currentRotation by remember { mutableFloatStateOf(0f) }
val rotation = remember { Animatable(currentRotation) }
LaunchedEffect(isExpanded) {
rotation.animateTo(
targetValue = if (isExpanded) 180f else 0f,
animationSpec = tween(200, easing = LinearEasing)
) {
currentRotation = value
}
}
Icon(
imageVector = Icons.Outlined.ExpandMore,
contentDescription = null,
modifier = Modifier
.size(32.dp)
.rotate(currentRotation)
.clickable {
isExpanded = !isExpanded
},
tint = MaterialTheme.colorScheme.onSurface,
)
}
AnimatedVisibility(
visible = isExpanded,
enter = expandVertically(),
exit = shrinkVertically()
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
season.episodes.forEach { episode ->
EpisodeItem(episode = episode)
}
}
}
}
@Composable
private fun EpisodeItem(episode: Episode) {
Card(
shape = RoundedCornerShape(10.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
modifier = Modifier
.fillMaxWidth()
.clickable {
}
) {
Box(
modifier = Modifier.height(112.dp)
) {
episode.stillPath?.let {
// Cloudy(
// modifier = Modifier.background(Color.Black.copy(alpha = 0.4f))
// ) {
AsyncImage(
model = TmdbUtils.getFullEpisodeStillPath(it),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier
.blur(radius = 10.dp)
.fillMaxWidth()
.wrapContentHeight()
)
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.4f))
.blur(radius = 5.dp)
)
// }
}
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.padding(12.dp)
) {
val textColor = episode.stillPath?.let { Color.White } ?: if (isSystemInDarkTheme()) Color.White else Color.Black
Text(
text = "S${episode.seasonNumber}E${episode.episodeNumber}${episode.name}",
color = textColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
TmdbUtils.convertEpisodeDate(episode.airDate)?.let {
Text(
text = it,
fontStyle = FontStyle.Italic,
fontSize = 12.sp,
color = textColor
)
}
Text(
text = episode.overview,
overflow = TextOverflow.Ellipsis,
color = textColor
)
}
}
}
}

View File

@@ -33,8 +33,8 @@ sealed class MediaTabNavItem(
}
companion object {
private val MovieItems = listOf(NowPlaying, Popular, Trending, Upcoming, TopRated)
private val TvItems = listOf(OnTheAir, Popular, Trending, AiringToday, TopRated)
private val MovieItems = listOf(Trending, NowPlaying, Popular, Upcoming, TopRated)
private val TvItems = listOf(Trending, OnTheAir, Popular, AiringToday, TopRated)
fun itemsForType(type: MediaViewType): List<MediaTabNavItem> {
return when (type) {

View File

@@ -242,15 +242,12 @@ object TmdbUtils {
return "$${thousands}"
}
fun convertEpisodeDate(inDate: String?): String? {
fun convertEpisodeDate(inDate: Date?): String? {
if (inDate == null) {
return null
}
val origFormat = SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault())
val outFormat = SimpleDateFormat("MMMM dd, yyyy", java.util.Locale.getDefault())
return origFormat.parse(inDate)?.let { outFormat.format(it) }
return outFormat.format(inDate)
}
fun toDate(releaseDate: String): Date {

View File

@@ -0,0 +1,10 @@
package com.owenlejeune.tvtime.utils.types
import com.google.gson.annotations.SerializedName
enum class Gender(val rawValue: Int) {
@SerializedName("1")
MALE(1),
@SerializedName("2")
FEMALE(2)
}