mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-19 10:11:13 -05:00
refactor common details view code
This commit is contained in:
@@ -3,6 +3,7 @@ package com.owenlejeune.tvtime.api.tmdb
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
|
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem
|
import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.model.HomePageResponse
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
interface DetailService {
|
interface DetailService {
|
||||||
@@ -13,4 +14,6 @@ interface DetailService {
|
|||||||
|
|
||||||
suspend fun getCastAndCrew(id: Int): Response<CastAndCrew>
|
suspend fun getCastAndCrew(id: Int): Response<CastAndCrew>
|
||||||
|
|
||||||
|
suspend fun getSimilar(id: Int, page: Int): Response<out HomePageResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -32,4 +32,7 @@ interface MoviesApi {
|
|||||||
@GET("movie/{id}/release_dates")
|
@GET("movie/{id}/release_dates")
|
||||||
suspend fun getReleaseDates(@Path("id") id: Int): Response<MovieReleaseResults>
|
suspend fun getReleaseDates(@Path("id") id: Int): Response<MovieReleaseResults>
|
||||||
|
|
||||||
|
@GET("movie/{id}/recommendations")
|
||||||
|
suspend fun getSimilarMovies(@Path("id") id: Int, @Query("page") page: Int = 1): Response<HomePageMoviesResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -40,4 +40,8 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
|||||||
return service.getCastAndCrew(id)
|
return service.getCastAndCrew(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getSimilar(id: Int, page: Int): Response<out HomePageResponse> {
|
||||||
|
return service.getSimilarMovies(id, page)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -32,4 +32,7 @@ interface TvApi {
|
|||||||
@GET("tv/{id}/content_ratings")
|
@GET("tv/{id}/content_ratings")
|
||||||
suspend fun getContentRatings(@Path("id") id: Int): Response<TvContentRatings>
|
suspend fun getContentRatings(@Path("id") id: Int): Response<TvContentRatings>
|
||||||
|
|
||||||
|
@GET("tv/{id}/similar")
|
||||||
|
suspend fun getSimilarTvShows(@Path("id") id: Int, @Query("page") page: Int = 1): Response<HomePageTvResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -39,4 +39,8 @@ class TvService: KoinComponent, DetailService, HomePageService {
|
|||||||
suspend fun getContentRatings(id: Int): Response<TvContentRatings> {
|
suspend fun getContentRatings(id: Int): Response<TvContentRatings> {
|
||||||
return service.getContentRatings(id)
|
return service.getContentRatings(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getSimilar(id: Int, page: Int): Response<out HomePageResponse> {
|
||||||
|
return service.getSimilarTvShows(id, page)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.owenlejeune.tvtime.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.Card
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import coil.compose.rememberImagePainter
|
||||||
|
import coil.transform.RoundedCornersTransformation
|
||||||
|
import com.owenlejeune.tvtime.R
|
||||||
|
import com.owenlejeune.tvtime.extensions.dpToPx
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ContentCard(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
title: String? = null,
|
||||||
|
backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
content: @Composable () -> Unit = {}
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight(),
|
||||||
|
shape = RoundedCornerShape(10.dp),
|
||||||
|
backgroundColor = backgroundColor,
|
||||||
|
elevation = 8.dp
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
|
title?.let {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
modifier = Modifier.padding(start = 12.dp, top = 8.dp),
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ImageTextCard(
|
||||||
|
title: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
subtitle: String? = null,
|
||||||
|
imageUrl: String? = null,
|
||||||
|
noDataImage: Int = R.drawable.placeholder,
|
||||||
|
placeholder: Int = R.drawable.placeholder,
|
||||||
|
titleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
subtitleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.padding(end = 12.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(width = 120.dp, height = 180.dp),
|
||||||
|
painter = rememberImagePainter(
|
||||||
|
data = imageUrl ?: noDataImage,
|
||||||
|
builder = {
|
||||||
|
transformations(RoundedCornersTransformation(5f.dpToPx(context)))
|
||||||
|
placeholder(placeholder)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
MinLinesText(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 5.dp),
|
||||||
|
minLines = 2,
|
||||||
|
text = title,
|
||||||
|
color = titleTextColor,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
subtitle?.let {
|
||||||
|
MinLinesText(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
minLines = 2,
|
||||||
|
text = subtitle,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = subtitleTextColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
app/src/main/java/com/owenlejeune/tvtime/ui/components/Colors.kt
Normal file
103
app/src/main/java/com/owenlejeune/tvtime/ui/components/Colors.kt
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package com.owenlejeune.tvtime.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.material.ContentAlpha
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.compositeOver
|
||||||
|
|
||||||
|
|
||||||
|
class CustomSwitchColors private constructor(
|
||||||
|
val lightUncheckedTrackColor: Color,
|
||||||
|
val darkUncheckedTrackColor: Color,
|
||||||
|
val lightUncheckedThumbColor: Color,
|
||||||
|
val darkUncheckedThumbColor: Color,
|
||||||
|
val lightCheckedTrackColor: Color,
|
||||||
|
val darkCheckedTrackColor: Color,
|
||||||
|
val lightCheckedThumbColor: Color,
|
||||||
|
val darkCheckedThumbColor: Color,
|
||||||
|
val lightDisabledTrackColor: Color,
|
||||||
|
val darkDisabledTrackColor: Color,
|
||||||
|
val lightDisabledThumbColor: Color,
|
||||||
|
val darkDisabledThumbColor: Color
|
||||||
|
){
|
||||||
|
companion object {
|
||||||
|
@Composable
|
||||||
|
fun topLevelColors(
|
||||||
|
lightUncheckedTrackColor: Color = MaterialTheme.colorScheme.outline,
|
||||||
|
darkUncheckedTrackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
lightUncheckedThumbColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
darkUncheckedThumbColor: Color = MaterialTheme.colorScheme.outline,
|
||||||
|
lightCheckedTrackColor: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
darkCheckedTrackColor: Color = MaterialTheme.colorScheme.outline,
|
||||||
|
lightCheckedThumbColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
darkCheckedThumbColor: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
lightDisabledTrackColor: Color = lightUncheckedTrackColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface),
|
||||||
|
darkDisabledTrackColor: Color = darkUncheckedTrackColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface),
|
||||||
|
lightDisabledThumbColor: Color = lightUncheckedThumbColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface),
|
||||||
|
darkDisabledThumbColor: Color = darkUncheckedThumbColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface)
|
||||||
|
): CustomSwitchColors {
|
||||||
|
return CustomSwitchColors(
|
||||||
|
lightUncheckedTrackColor,
|
||||||
|
darkUncheckedTrackColor,
|
||||||
|
lightUncheckedThumbColor,
|
||||||
|
darkUncheckedThumbColor,
|
||||||
|
lightCheckedTrackColor,
|
||||||
|
darkCheckedTrackColor,
|
||||||
|
lightCheckedThumbColor,
|
||||||
|
darkCheckedThumbColor,
|
||||||
|
lightDisabledTrackColor,
|
||||||
|
darkDisabledTrackColor,
|
||||||
|
lightDisabledThumbColor,
|
||||||
|
darkDisabledThumbColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun standardColors(
|
||||||
|
lightUncheckedTrackColor: Color = MaterialTheme.colorScheme.outline,
|
||||||
|
darkUncheckedTrackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
lightUncheckedThumbColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
darkUncheckedThumbColor: Color = MaterialTheme.colorScheme.outline,
|
||||||
|
lightCheckedTrackColor: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
darkCheckedTrackColor: Color = MaterialTheme.colorScheme.secondaryContainer,
|
||||||
|
lightCheckedThumbColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
darkCheckedThumbColor: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
lightDisabledTrackColor: Color = lightUncheckedTrackColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface),
|
||||||
|
darkDisabledTrackColor: Color = darkUncheckedTrackColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface),
|
||||||
|
lightDisabledThumbColor: Color = lightUncheckedThumbColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface),
|
||||||
|
darkDisabledThumbColor: Color = darkUncheckedThumbColor
|
||||||
|
.copy(alpha = ContentAlpha.disabled)
|
||||||
|
.compositeOver(MaterialTheme.colorScheme.surface)
|
||||||
|
): CustomSwitchColors {
|
||||||
|
return CustomSwitchColors(
|
||||||
|
lightUncheckedTrackColor,
|
||||||
|
darkUncheckedTrackColor,
|
||||||
|
lightUncheckedThumbColor,
|
||||||
|
darkUncheckedThumbColor,
|
||||||
|
lightCheckedTrackColor,
|
||||||
|
darkCheckedTrackColor,
|
||||||
|
lightCheckedThumbColor,
|
||||||
|
darkCheckedThumbColor,
|
||||||
|
lightDisabledTrackColor,
|
||||||
|
darkDisabledTrackColor,
|
||||||
|
lightDisabledThumbColor,
|
||||||
|
darkDisabledThumbColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,6 @@ import androidx.compose.foundation.text.BasicTextField
|
|||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.Card
|
import androidx.compose.material.Card
|
||||||
import androidx.compose.material.ContentAlpha
|
|
||||||
import androidx.compose.material.Switch
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@@ -28,7 +26,6 @@ import androidx.compose.ui.geometry.CornerRadius
|
|||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
import androidx.compose.ui.graphics.compositeOver
|
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
@@ -95,101 +92,6 @@ fun TopLevelSwitch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomSwitchColors private constructor(
|
|
||||||
val lightUncheckedTrackColor: Color,
|
|
||||||
val darkUncheckedTrackColor: Color,
|
|
||||||
val lightUncheckedThumbColor: Color,
|
|
||||||
val darkUncheckedThumbColor: Color,
|
|
||||||
val lightCheckedTrackColor: Color,
|
|
||||||
val darkCheckedTrackColor: Color,
|
|
||||||
val lightCheckedThumbColor: Color,
|
|
||||||
val darkCheckedThumbColor: Color,
|
|
||||||
val lightDisabledTrackColor: Color,
|
|
||||||
val darkDisabledTrackColor: Color,
|
|
||||||
val lightDisabledThumbColor: Color,
|
|
||||||
val darkDisabledThumbColor: Color
|
|
||||||
){
|
|
||||||
companion object {
|
|
||||||
@Composable
|
|
||||||
fun topLevelColors(
|
|
||||||
lightUncheckedTrackColor: Color = MaterialTheme.colorScheme.outline,
|
|
||||||
darkUncheckedTrackColor: Color =MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
lightUncheckedThumbColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
darkUncheckedThumbColor: Color = MaterialTheme.colorScheme.outline,
|
|
||||||
lightCheckedTrackColor: Color = MaterialTheme.colorScheme.primary,
|
|
||||||
darkCheckedTrackColor: Color = MaterialTheme.colorScheme.outline,
|
|
||||||
lightCheckedThumbColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
|
||||||
darkCheckedThumbColor: Color = MaterialTheme.colorScheme.primary,
|
|
||||||
lightDisabledTrackColor: Color = lightUncheckedTrackColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface),
|
|
||||||
darkDisabledTrackColor: Color = darkUncheckedTrackColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface),
|
|
||||||
lightDisabledThumbColor: Color = lightUncheckedThumbColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface),
|
|
||||||
darkDisabledThumbColor: Color = darkUncheckedThumbColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface)
|
|
||||||
): CustomSwitchColors {
|
|
||||||
return CustomSwitchColors(
|
|
||||||
lightUncheckedTrackColor,
|
|
||||||
darkUncheckedTrackColor,
|
|
||||||
lightUncheckedThumbColor,
|
|
||||||
darkUncheckedThumbColor,
|
|
||||||
lightCheckedTrackColor,
|
|
||||||
darkCheckedTrackColor,
|
|
||||||
lightCheckedThumbColor,
|
|
||||||
darkCheckedThumbColor,
|
|
||||||
lightDisabledTrackColor,
|
|
||||||
darkDisabledTrackColor,
|
|
||||||
lightDisabledThumbColor,
|
|
||||||
darkDisabledThumbColor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun standardColors(
|
|
||||||
lightUncheckedTrackColor: Color = MaterialTheme.colorScheme.outline,
|
|
||||||
darkUncheckedTrackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
lightUncheckedThumbColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
darkUncheckedThumbColor: Color = MaterialTheme.colorScheme.outline,
|
|
||||||
lightCheckedTrackColor: Color = MaterialTheme.colorScheme.primary,
|
|
||||||
darkCheckedTrackColor: Color = MaterialTheme.colorScheme.secondaryContainer,
|
|
||||||
lightCheckedThumbColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
|
||||||
darkCheckedThumbColor: Color = MaterialTheme.colorScheme.primary,
|
|
||||||
lightDisabledTrackColor: Color = lightUncheckedTrackColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface),
|
|
||||||
darkDisabledTrackColor: Color = darkUncheckedTrackColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface),
|
|
||||||
lightDisabledThumbColor: Color = lightUncheckedThumbColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface),
|
|
||||||
darkDisabledThumbColor: Color = darkUncheckedThumbColor
|
|
||||||
.copy(alpha = ContentAlpha.disabled)
|
|
||||||
.compositeOver(MaterialTheme.colorScheme.surface)
|
|
||||||
): CustomSwitchColors {
|
|
||||||
return CustomSwitchColors(
|
|
||||||
lightUncheckedTrackColor,
|
|
||||||
darkUncheckedTrackColor,
|
|
||||||
lightUncheckedThumbColor,
|
|
||||||
darkUncheckedThumbColor,
|
|
||||||
lightCheckedTrackColor,
|
|
||||||
darkCheckedTrackColor,
|
|
||||||
lightCheckedThumbColor,
|
|
||||||
darkCheckedThumbColor,
|
|
||||||
lightDisabledTrackColor,
|
|
||||||
darkDisabledTrackColor,
|
|
||||||
lightDisabledThumbColor,
|
|
||||||
darkDisabledThumbColor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CustomSwitch(
|
fun CustomSwitch(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.Card
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -21,24 +18,17 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.constraintlayout.compose.ConstraintLayout
|
import androidx.constraintlayout.compose.ConstraintLayout
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.rememberImagePainter
|
|
||||||
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.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.ui.components.*
|
||||||
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.PosterItem
|
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -160,12 +150,7 @@ private fun BackButton(modifier: Modifier, appNavController: NavController) {
|
|||||||
onClick = { appNavController.popBackStack() },
|
onClick = { appNavController.popBackStack() },
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.background(
|
.background(
|
||||||
brush = Brush.radialGradient(
|
brush = Brush.radialGradient(colors = listOf(Color.Black, Color.Transparent))
|
||||||
colors = listOf(
|
|
||||||
Color.Black,
|
|
||||||
Color.Transparent
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.wrapContentSize()
|
.wrapContentSize()
|
||||||
) {
|
) {
|
||||||
@@ -178,7 +163,8 @@ private fun BackButton(modifier: Modifier, appNavController: NavController) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentColumn(modifier: Modifier,
|
private fun ContentColumn(
|
||||||
|
modifier: Modifier,
|
||||||
itemId: Int?,
|
itemId: Int?,
|
||||||
mediaItem: MutableState<DetailedItem?>,
|
mediaItem: MutableState<DetailedItem?>,
|
||||||
service: DetailService,
|
service: DetailService,
|
||||||
@@ -190,7 +176,6 @@ private fun ContentColumn(modifier: Modifier,
|
|||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (mediaType == MediaViewType.MOVIE) {
|
if (mediaType == MediaViewType.MOVIE) {
|
||||||
MiscMovieDetails(mediaItem = mediaItem, service as MoviesService)
|
MiscMovieDetails(mediaItem = mediaItem, service as MoviesService)
|
||||||
} else {
|
} else {
|
||||||
@@ -198,10 +183,12 @@ private fun ContentColumn(modifier: Modifier,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mediaItem.value?.overview?.isNotEmpty() == true) {
|
if (mediaItem.value?.overview?.isNotEmpty() == true) {
|
||||||
OverviewCard(mediaItem = mediaItem)
|
OverviewCard(mediaItem = mediaItem, modifier = Modifier.padding(bottom = 16.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
CastCard(itemId = itemId, service = service)
|
CastCard(itemId = itemId, service = service, modifier = Modifier.padding(bottom = 16.dp))
|
||||||
|
|
||||||
|
SimilarContent(itemId = itemId, service = service)//, modifier = Modifier.padding(bottom = 16.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,15 +273,9 @@ private fun MiscDetails(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OverviewCard(mediaItem: MutableState<DetailedItem?>) {
|
private fun OverviewCard(mediaItem: MutableState<DetailedItem?>, modifier: Modifier = Modifier) {
|
||||||
Card(
|
ContentCard(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
|
||||||
.wrapContentHeight()
|
|
||||||
.padding(bottom = 16.dp),
|
|
||||||
shape = RoundedCornerShape(10.dp),
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
elevation = 8.dp
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -309,7 +290,7 @@ private fun OverviewCard(mediaItem: MutableState<DetailedItem?>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CastCard(itemId: Int?, service: DetailService) {
|
private fun CastCard(itemId: Int?, service: DetailService, modifier: Modifier = Modifier) {
|
||||||
val castAndCrew = remember { mutableStateOf<CastAndCrew?>(null) }
|
val castAndCrew = remember { mutableStateOf<CastAndCrew?>(null) }
|
||||||
itemId?.let {
|
itemId?.let {
|
||||||
if (castAndCrew.value == null) {
|
if (castAndCrew.value == null) {
|
||||||
@@ -317,76 +298,74 @@ private fun CastCard(itemId: Int?, service: DetailService) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Card(
|
ContentCard(
|
||||||
modifier = Modifier
|
modifier = modifier,
|
||||||
.fillMaxWidth()
|
title = stringResource(R.string.cast_label),
|
||||||
.wrapContentHeight(),
|
|
||||||
shape = RoundedCornerShape(10.dp),
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.primary,
|
backgroundColor = MaterialTheme.colorScheme.primary,
|
||||||
elevation = 8.dp
|
textColor = MaterialTheme.colorScheme.background
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.cast_label),
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
modifier = Modifier.padding(start = 12.dp, top = 12.dp)
|
|
||||||
)
|
|
||||||
LazyRow(modifier = Modifier
|
LazyRow(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(12.dp)
|
.padding(12.dp)
|
||||||
) {
|
) {
|
||||||
items(castAndCrew.value?.cast?.size ?: 0) { i ->
|
items(castAndCrew.value?.cast?.size ?: 0) { i ->
|
||||||
val castMember = castAndCrew.value!!.cast[i]
|
val castMember = castAndCrew.value!!.cast[i]
|
||||||
|
|
||||||
CastCrewCard(person = castMember)
|
CastCrewCard(person = castMember)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CastCrewCard(person: Person) {
|
private fun CastCrewCard(person: Person) {
|
||||||
val context = LocalContext.current
|
ImageTextCard(
|
||||||
Column(
|
title = person.name,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(124.dp)
|
.width(124.dp)
|
||||||
.wrapContentHeight()
|
.wrapContentHeight(),
|
||||||
.padding(end = 12.dp)
|
subtitle = when (person) {
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(width = 120.dp, height = 180.dp),
|
|
||||||
painter = rememberImagePainter(
|
|
||||||
data = TmdbUtils.getFullPersonImagePath(person) ?: R.drawable.no_person_photo,
|
|
||||||
builder = {
|
|
||||||
transformations(RoundedCornersTransformation(5f.dpToPx(context)))
|
|
||||||
placeholder(R.drawable.placeholder)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
contentDescription = ""
|
|
||||||
)
|
|
||||||
MinLinesText(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(top = 5.dp),
|
|
||||||
minLines = 2,
|
|
||||||
text = person.name,
|
|
||||||
color = MaterialTheme.colorScheme.onPrimary,
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
MinLinesText(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth(),
|
|
||||||
minLines = 2,
|
|
||||||
text = when (person) {
|
|
||||||
is CastMember -> person.character
|
is CastMember -> person.character
|
||||||
is CrewMember -> person.job
|
is CrewMember -> person.job
|
||||||
else -> ""
|
else -> null
|
||||||
},
|
},
|
||||||
style = MaterialTheme.typography.bodySmall
|
imageUrl = TmdbUtils.getFullPersonImagePath(person),
|
||||||
|
noDataImage = R.drawable.no_person_photo,
|
||||||
|
titleTextColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
subtitleTextColor = Color.Unspecified
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SimilarContent(itemId: Int?, service: DetailService, modifier: Modifier = Modifier) {
|
||||||
|
val similarContent = remember { mutableStateOf<HomePageResponse?>(null) }
|
||||||
|
itemId?.let {
|
||||||
|
if (similarContent.value == null) {
|
||||||
|
fetchSimilarContent(itemId, service, similarContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentCard(
|
||||||
|
modifier = modifier,
|
||||||
|
title = "Recommended"
|
||||||
|
) {
|
||||||
|
LazyRow(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(12.dp)
|
||||||
|
) {
|
||||||
|
items(similarContent.value?.results?.size ?: 0) { i ->
|
||||||
|
val content = similarContent.value!!.results[i]
|
||||||
|
|
||||||
|
ImageTextCard(
|
||||||
|
title = content.title,
|
||||||
|
modifier = Modifier
|
||||||
|
.width(124.dp)
|
||||||
|
.wrapContentHeight(),
|
||||||
|
imageUrl = TmdbUtils.getFullPosterPath(content)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchMediaItem(id: Int, service: DetailService, mediaItem: MutableState<DetailedItem?>) {
|
private fun fetchMediaItem(id: Int, service: DetailService, mediaItem: MutableState<DetailedItem?>) {
|
||||||
@@ -445,3 +424,14 @@ private fun fetchTvContentRating(id: Int, service: TvService, contentRating: Mut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fetchSimilarContent(id: Int, service: DetailService, similarContent: MutableState<HomePageResponse?>) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val results = service.getSimilar(id, 1)
|
||||||
|
if (results.isSuccessful) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
similarContent.value = results.body()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user