mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-20 02:30:53 -05:00
add more details for movies
This commit is contained in:
@@ -64,6 +64,7 @@ dependencies {
|
|||||||
implementation "androidx.activity:activity-compose:${Versions.activity_compose}"
|
implementation "androidx.activity:activity-compose:${Versions.activity_compose}"
|
||||||
implementation "com.google.accompanist:accompanist-systemuicontroller:${Versions.compose_accompanist}"
|
implementation "com.google.accompanist:accompanist-systemuicontroller:${Versions.compose_accompanist}"
|
||||||
implementation "com.google.accompanist:accompanist-pager:${Versions.compose_accompanist}"
|
implementation "com.google.accompanist:accompanist-pager:${Versions.compose_accompanist}"
|
||||||
|
implementation "com.google.accompanist:accompanist-flowlayout:${Versions.compose_accompanist}"
|
||||||
implementation "androidx.navigation:navigation-compose:${Versions.compose_navigation}"
|
implementation "androidx.navigation:navigation-compose:${Versions.compose_navigation}"
|
||||||
implementation "androidx.paging:paging-compose:${Versions.compose_paging}"
|
implementation "androidx.paging:paging-compose:${Versions.compose_paging}"
|
||||||
implementation "androidx.constraintlayout:constraintlayout-compose:${Versions.compose_constraint_layout}"
|
implementation "androidx.constraintlayout:constraintlayout-compose:${Versions.compose_constraint_layout}"
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb
|
package com.owenlejeune.tvtime.api.tmdb
|
||||||
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
|
import com.owenlejeune.tvtime.api.tmdb.model.*
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.DetailedMovie
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.PopularMoviesResponse
|
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
@@ -23,4 +20,7 @@ interface MoviesApi {
|
|||||||
@GET("movie/{id}/credits")
|
@GET("movie/{id}/credits")
|
||||||
suspend fun getCastAndCrew(@Path("id") id: Int): Response<CastAndCrew>
|
suspend fun getCastAndCrew(@Path("id") id: Int): Response<CastAndCrew>
|
||||||
|
|
||||||
|
@GET("movie/{id}/release_dates")
|
||||||
|
suspend fun getReleaseDates(@Path("id") id: Int): Response<MovieReleaseResults>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb
|
package com.owenlejeune.tvtime.api.tmdb
|
||||||
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
|
import com.owenlejeune.tvtime.api.tmdb.model.*
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem
|
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
@@ -10,7 +8,13 @@ class MoviesService: KoinComponent, DetailService {
|
|||||||
|
|
||||||
private val service by lazy { TmdbClient().createMovieService() }
|
private val service by lazy { TmdbClient().createMovieService() }
|
||||||
|
|
||||||
suspend fun getPopularMovies(page: Int = 1) = service.getPopularMovies(page)
|
suspend fun getPopularMovies(page: Int = 1): Response<PopularMoviesResponse> {
|
||||||
|
return service.getPopularMovies(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getReleaseDates(id: Int): Response<MovieReleaseResults> {
|
||||||
|
return service.getReleaseDates(id)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getById(id: Int): Response<out DetailedItem> {
|
override suspend fun getById(id: Int): Response<out DetailedItem> {
|
||||||
return service.getMovieById(id)
|
return service.getMovieById(id)
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb
|
|
||||||
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.Image
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.Person
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.TmdbItem
|
|
||||||
|
|
||||||
object TmdbUtils {
|
|
||||||
|
|
||||||
private const val POSTER_BASE = "https://image.tmdb.org/t/p/original"
|
|
||||||
private const val BACKDROP_BASE = "https://www.themoviedb.org/t/p/original"
|
|
||||||
private const val PERSON_BASE = "https://www.themoviedb.org/t/p/w600_and_h900_bestv2"
|
|
||||||
|
|
||||||
fun getFullPosterPath(posterPath: String?): String? {
|
|
||||||
return posterPath?.let { "https://image.tmdb.org/t/p/original${posterPath}" }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFullPosterPath(tmdbItem: TmdbItem?): String? {
|
|
||||||
return tmdbItem?.let { getFullPosterPath(tmdbItem.posterPath) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFullPosterPath(image: Image): String? {
|
|
||||||
return getFullPosterPath(image.filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFullBackdropPath(backdropPath: String?): String? {
|
|
||||||
return backdropPath?.let { "https://www.themoviedb.org/t/p/original${backdropPath}" }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFullBackdropPath(detailItem: DetailedItem?): String? {
|
|
||||||
return detailItem?.let { getFullBackdropPath(detailItem.backdropPath) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFullBackdropPath(image: Image): String? {
|
|
||||||
return getFullBackdropPath(image.filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFullPersonImagePath(path: String?): String? {
|
|
||||||
return path?.let { "https://www.themoviedb.org/t/p/w600_and_h900_bestv2${path}" }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFullPersonImagePath(person: Person): String? {
|
|
||||||
return getFullPersonImagePath(person.profilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -15,6 +15,7 @@ class DetailedTv(
|
|||||||
@SerializedName("vote_average") override val voteAverage: Float,
|
@SerializedName("vote_average") override val voteAverage: Float,
|
||||||
@SerializedName("created_by") val createdBy: List<Person>,
|
@SerializedName("created_by") val createdBy: List<Person>,
|
||||||
@SerializedName("first_air_date") val firstAirDate: String,
|
@SerializedName("first_air_date") val firstAirDate: String,
|
||||||
|
@SerializedName("last_air_date") val lastAirDate: String,
|
||||||
@SerializedName("in_production") val inProduction: Boolean,
|
@SerializedName("in_production") val inProduction: Boolean,
|
||||||
@SerializedName("networks") val networks: List<Network>,
|
@SerializedName("networks") val networks: List<Network>,
|
||||||
@SerializedName("number_of_episodes") val numberOfEpisodes: Int,
|
@SerializedName("number_of_episodes") val numberOfEpisodes: Int,
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.owenlejeune.tvtime.api.tmdb.model
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class MovieReleaseResults(
|
||||||
|
@SerializedName("results") val releaseDates: List<ReleaseDateResult>
|
||||||
|
) {
|
||||||
|
|
||||||
|
inner class ReleaseDateResult(
|
||||||
|
@SerializedName("iso_3166_1") val region: String,
|
||||||
|
@SerializedName("release_dates") val releaseDates: List<ReleaseDate>
|
||||||
|
)
|
||||||
|
|
||||||
|
inner class ReleaseDate(
|
||||||
|
@SerializedName("certification") val certification: String,
|
||||||
|
@SerializedName("release_date") val releaseDate: String
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.owenlejeune.tvtime.extensions.listItems
|
import com.owenlejeune.tvtime.extensions.listItems
|
||||||
|
import com.owenlejeune.tvtime.utils.ComposeUtils
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PaletteView() {
|
fun PaletteView() {
|
||||||
@@ -57,8 +58,9 @@ private fun PaletteItemView(surfaceColor: Color, textColor: Color, text: String)
|
|||||||
shape = RoundedCornerShape(30.dp),
|
shape = RoundedCornerShape(30.dp),
|
||||||
backgroundColor = surfaceColor
|
backgroundColor = surfaceColor
|
||||||
) {
|
) {
|
||||||
|
val hexString = ComposeUtils.colorToHexString(surfaceColor)
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = "${text}\n${hexString}",
|
||||||
color = textColor,
|
color = textColor,
|
||||||
modifier = Modifier.padding(30.dp, 12.dp)
|
modifier = Modifier.padding(30.dp, 12.dp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,12 +30,11 @@ import com.google.accompanist.pager.ExperimentalPagerApi
|
|||||||
import com.google.accompanist.pager.HorizontalPager
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.TmdbItem
|
import com.owenlejeune.tvtime.api.tmdb.model.TmdbItem
|
||||||
import com.owenlejeune.tvtime.extensions.dpToPx
|
import com.owenlejeune.tvtime.extensions.dpToPx
|
||||||
import com.owenlejeune.tvtime.extensions.listItems
|
import com.owenlejeune.tvtime.extensions.listItems
|
||||||
import kotlinx.coroutines.*
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import androidx.compose.animation.core.animateFloatAsState
|
|||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.selection.toggleable
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.Card
|
import androidx.compose.material.Card
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -36,6 +38,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.google.accompanist.flowlayout.FlowRow
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TopLevelSwitch(
|
fun TopLevelSwitch(
|
||||||
@@ -206,4 +209,60 @@ fun MinLinesText(
|
|||||||
onTextLayout = onTextLayout,
|
onTextLayout = onTextLayout,
|
||||||
style = style
|
style = style
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Chip(
|
||||||
|
text: String,
|
||||||
|
style: TextStyle = MaterialTheme.typography.bodySmall,
|
||||||
|
isSelected: Boolean = true,
|
||||||
|
onSelectionChanged: (String) -> Unit = {}
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.padding(4.dp),
|
||||||
|
shadowElevation = 8.dp,
|
||||||
|
shape = RoundedCornerShape(5.dp),
|
||||||
|
color = if (isSelected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.secondary
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.toggleable(
|
||||||
|
value = isSelected,
|
||||||
|
onValueChange = {
|
||||||
|
onSelectionChanged(text)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = style,
|
||||||
|
color = if (isSelected) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSecondary,
|
||||||
|
modifier = Modifier.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChipGroup(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
chips: List<String> = emptyList(),
|
||||||
|
onSelectedChanged: (String) -> Unit = {},
|
||||||
|
) {
|
||||||
|
FlowRow(
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
chips.forEach { chip ->
|
||||||
|
Chip(
|
||||||
|
text = chip,
|
||||||
|
onSelectionChanged = onSelectedChanged
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun ChipPreview() {
|
||||||
|
Chip("Test Chip")
|
||||||
}
|
}
|
||||||
@@ -28,11 +28,12 @@ import coil.transform.RoundedCornersTransformation
|
|||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.DetailService
|
import com.owenlejeune.tvtime.api.tmdb.DetailService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.MoviesService
|
import com.owenlejeune.tvtime.api.tmdb.MoviesService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import com.owenlejeune.tvtime.api.tmdb.TvService
|
import com.owenlejeune.tvtime.api.tmdb.TvService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.*
|
import com.owenlejeune.tvtime.api.tmdb.model.*
|
||||||
import com.owenlejeune.tvtime.extensions.dpToPx
|
import com.owenlejeune.tvtime.extensions.dpToPx
|
||||||
import com.owenlejeune.tvtime.ui.components.BackdropImage
|
import com.owenlejeune.tvtime.ui.components.BackdropImage
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ChipGroup
|
||||||
import com.owenlejeune.tvtime.ui.components.MinLinesText
|
import com.owenlejeune.tvtime.ui.components.MinLinesText
|
||||||
import com.owenlejeune.tvtime.ui.components.PosterItem
|
import com.owenlejeune.tvtime.ui.components.PosterItem
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -46,7 +47,6 @@ fun DetailView(
|
|||||||
itemId: Int?,
|
itemId: Int?,
|
||||||
type: DetailViewType
|
type: DetailViewType
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
val service = when(type) {
|
val service = when(type) {
|
||||||
DetailViewType.MOVIE -> MoviesService()
|
DetailViewType.MOVIE -> MoviesService()
|
||||||
DetailViewType.TV -> TvService()
|
DetailViewType.TV -> TvService()
|
||||||
@@ -109,11 +109,12 @@ fun DetailView(
|
|||||||
|
|
||||||
ContentColumn(
|
ContentColumn(
|
||||||
modifier = Modifier.constrainAs(contentColumn) {
|
modifier = Modifier.constrainAs(contentColumn) {
|
||||||
top.linkTo(backdropImage.bottom, margin = 8.dp)
|
top.linkTo(backdropImage.bottom)//, margin = 8.dp)
|
||||||
},
|
},
|
||||||
itemId = itemId,
|
itemId = itemId,
|
||||||
mediaItem = mediaItem,
|
mediaItem = mediaItem,
|
||||||
service = service
|
service = service,
|
||||||
|
mediaType = type
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,27 +177,84 @@ private fun BackButton(modifier: Modifier, appNavController: NavController) {
|
|||||||
private fun ContentColumn(modifier: Modifier,
|
private fun ContentColumn(modifier: Modifier,
|
||||||
itemId: Int?,
|
itemId: Int?,
|
||||||
mediaItem: MutableState<DetailedItem?>,
|
mediaItem: MutableState<DetailedItem?>,
|
||||||
service: DetailService
|
service: DetailService,
|
||||||
|
mediaType: DetailViewType
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(horizontal = 16.dp)
|
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||||
) {
|
) {
|
||||||
|
if (mediaType == DetailViewType.MOVIE) {
|
||||||
|
MiscMovieDetails(mediaItem = mediaItem, service as MoviesService)
|
||||||
|
} else {
|
||||||
|
MiscTvDetails(mediaItem = mediaItem)
|
||||||
|
}
|
||||||
|
|
||||||
OverviewCard(mediaItem = mediaItem)
|
OverviewCard(mediaItem = mediaItem)
|
||||||
|
|
||||||
CastCard(itemId = itemId, service = service)
|
CastCard(itemId = itemId, service = service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MiscTvDetails(mediaItem: MutableState<DetailedItem?>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MiscMovieDetails(mediaItem: MutableState<DetailedItem?>, service: MoviesService) {
|
||||||
|
mediaItem.value?.let { mi ->
|
||||||
|
val movie = mi as DetailedMovie
|
||||||
|
|
||||||
|
val contentRating = remember { mutableStateOf("") }
|
||||||
|
fetchMovieContentRating(movie.id, service, contentRating)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(start = 8.dp, end = 10.dp, bottom = 8.dp)
|
||||||
|
) {
|
||||||
|
val releaseYear = TmdbUtils.getMovieReleaseYear(movie)
|
||||||
|
Text(text = releaseYear, color = MaterialTheme.colorScheme.onBackground)
|
||||||
|
val runtime = TmdbUtils.convertRuntimeToHoursMinutes(movie)
|
||||||
|
Text(
|
||||||
|
text = runtime,
|
||||||
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
modifier = Modifier.padding(start = 12.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = contentRating.value,
|
||||||
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
modifier = Modifier.padding(start = 12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ChipGroup(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(bottom = 8.dp),
|
||||||
|
chips = movie.genres.map { it.name }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OverviewCard(mediaItem: MutableState<DetailedItem?>) {
|
private fun OverviewCard(mediaItem: MutableState<DetailedItem?>) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(bottom = 12.dp),
|
.padding(bottom = 16.dp),
|
||||||
shape = RoundedCornerShape(10.dp),
|
shape = RoundedCornerShape(10.dp),
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
elevation = 8.dp
|
elevation = 8.dp
|
||||||
@@ -320,6 +378,18 @@ private fun fetchCastAndCrew(id: Int, service: DetailService, castAndCrew: Mutab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fetchMovieContentRating(id: Int, service: MoviesService, contentRating: MutableState<String>) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val results = service.getReleaseDates(id)
|
||||||
|
if (results.isSuccessful) {
|
||||||
|
val cr = TmdbUtils.getMovieRating(results.body())
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
contentRating.value = cr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class DetailViewType {
|
enum class DetailViewType {
|
||||||
MOVIE,
|
MOVIE,
|
||||||
TV
|
TV
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.owenlejeune.tvtime.utils
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
object ComposeUtils {
|
||||||
|
|
||||||
|
fun colorToHexString(color: Color): String {
|
||||||
|
val a = (color.alpha * 255f).toInt().toString(16).uppercase()
|
||||||
|
val r = (color.red * 255f).toInt().toString(16).uppercase()
|
||||||
|
val g = (color.green * 255f).toInt().toString(16).uppercase()
|
||||||
|
val b = (color.blue * 255f).toInt().toString(16).uppercase()
|
||||||
|
|
||||||
|
return "0x$a$r$g$b"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
87
app/src/main/java/com/owenlejeune/tvtime/utils/TmdbUtils.kt
Normal file
87
app/src/main/java/com/owenlejeune/tvtime/utils/TmdbUtils.kt
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package com.owenlejeune.tvtime.utils
|
||||||
|
|
||||||
|
import androidx.compose.ui.text.intl.Locale
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.model.*
|
||||||
|
|
||||||
|
object TmdbUtils {
|
||||||
|
|
||||||
|
private const val POSTER_BASE = "https://image.tmdb.org/t/p/original"
|
||||||
|
private const val BACKDROP_BASE = "https://www.themoviedb.org/t/p/original"
|
||||||
|
private const val PERSON_BASE = "https://www.themoviedb.org/t/p/w600_and_h900_bestv2"
|
||||||
|
|
||||||
|
fun getFullPosterPath(posterPath: String?): String? {
|
||||||
|
return posterPath?.let { "https://image.tmdb.org/t/p/original${posterPath}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFullPosterPath(tmdbItem: TmdbItem?): String? {
|
||||||
|
return tmdbItem?.let { getFullPosterPath(tmdbItem.posterPath) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFullPosterPath(image: Image): String? {
|
||||||
|
return getFullPosterPath(image.filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFullBackdropPath(backdropPath: String?): String? {
|
||||||
|
return backdropPath?.let { "https://www.themoviedb.org/t/p/original${backdropPath}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFullBackdropPath(detailItem: DetailedItem?): String? {
|
||||||
|
return detailItem?.let { getFullBackdropPath(detailItem.backdropPath) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFullBackdropPath(image: Image): String? {
|
||||||
|
return getFullBackdropPath(image.filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFullPersonImagePath(path: String?): String? {
|
||||||
|
return path?.let { "https://www.themoviedb.org/t/p/w600_and_h900_bestv2${path}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFullPersonImagePath(person: Person): String? {
|
||||||
|
return getFullPersonImagePath(person.profilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMovieReleaseYear(movie: DetailedMovie): String {
|
||||||
|
return movie.releaseDate.split("-")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTvStartYear(series: DetailedTv): String {
|
||||||
|
return series.firstAirDate.split("-")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTvEndYear(series: DetailedTv): String {
|
||||||
|
return series.lastAirDate.split("-")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertRuntimeToHoursMinutes(movie: DetailedMovie): String {
|
||||||
|
movie.runtime?.let { runtime ->
|
||||||
|
val hours = runtime / 60
|
||||||
|
val minutes = runtime % 60
|
||||||
|
return "${hours}h${minutes}"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMovieRating(releases: MovieReleaseResults?): String {
|
||||||
|
if (releases == null) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val defRegion = "US"
|
||||||
|
val currentRegion = Locale.current.language
|
||||||
|
val certifications = HashMap<String, String>()
|
||||||
|
releases.releaseDates.forEach { releaseDateResult ->
|
||||||
|
if (releaseDateResult.region == currentRegion || releaseDateResult.region == defRegion) {
|
||||||
|
val cert = releaseDateResult.releaseDates.firstOrNull { it.certification.isNotEmpty() }
|
||||||
|
if (cert != null) {
|
||||||
|
certifications[releaseDateResult.region] = cert.certification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (certifications.isNotEmpty()) {
|
||||||
|
return certifications[currentRegion] ?: certifications[defRegion] ?: ""
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user