add beginning person page implementation

This commit is contained in:
Owen LeJeune
2022-02-23 17:35:54 -05:00
parent a3e32383bd
commit 8946f72c8a
18 changed files with 357 additions and 60 deletions

View File

@@ -0,0 +1,30 @@
package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.DetailPerson
import com.owenlejeune.tvtime.api.tmdb.model.PersonCreditsResponse
import com.owenlejeune.tvtime.api.tmdb.model.PersonImageCollection
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
interface PeopleApi {
@GET("person/{id}")
suspend fun getPerson(@Path("id") id: Int): Response<DetailPerson>
// @GET("person/{id}/movie_credits")
// suspend fun getMovieCredits(@Path("id") id: Int)
//
// @GET("person/{id}/tv_credits")
// suspend fun getTvCredits(@Path("id") id: Int)
@GET("person/{id}/combined_credits")
suspend fun getCredits(@Path("id") id: Int): Response<PersonCreditsResponse>
@GET("person/{id}/images")
suspend fun getImages(@Path("id") id: Int): Response<PersonImageCollection>
// @GET("persons/{id}/tagged_images")
// suspend fun getTaggedImages(@Path("id") id: Int, @Query("page") page: Int = 1): Response<PersonImageCollection>
}

View File

@@ -0,0 +1,25 @@
package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.DetailPerson
import com.owenlejeune.tvtime.api.tmdb.model.PersonCreditsResponse
import com.owenlejeune.tvtime.api.tmdb.model.PersonImageCollection
import org.koin.core.component.KoinComponent
import retrofit2.Response
class PeopleService: KoinComponent {
private val service by lazy { TmdbClient().createPeopleService() }
suspend fun getPerson(id: Int): Response<DetailPerson> {
return service.getPerson(id)
}
suspend fun getCredits(id: Int): Response<PersonCreditsResponse> {
return service.getCredits(id)
}
suspend fun getImages(id: Int): Response<PersonImageCollection> {
return service.getImages(id)
}
}

View File

@@ -33,6 +33,10 @@ class TmdbClient: KoinComponent {
return client.create(TvApi::class.java) return client.create(TvApi::class.java)
} }
fun createPeopleService(): PeopleApi {
return client.create(PeopleApi::class.java)
}
private inner class TmdbInterceptor: Interceptor { private inner class TmdbInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey) val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey)

View File

@@ -5,9 +5,9 @@ import com.google.gson.annotations.SerializedName
class CastMember( class CastMember(
@SerializedName("character") val character: String, @SerializedName("character") val character: String,
@SerializedName("order") val order: Int, @SerializedName("order") val order: Int,
@SerializedName("credit_id") val creditId: String,
id: Int, id: Int,
creditId: String,
name: String, name: String,
gender: Int, gender: Int,
profilePath: String? profilePath: String?
): Person(id, creditId, name, gender, profilePath) ): Person(id, name, gender, profilePath)

View File

@@ -5,9 +5,9 @@ import com.google.gson.annotations.SerializedName
class CrewMember( class CrewMember(
@SerializedName("department") val department: String, @SerializedName("department") val department: String,
@SerializedName("job") val job: String, @SerializedName("job") val job: String,
@SerializedName("credit_id") val creditId: String,
id: Int, id: Int,
creditId: String,
name: String, name: String,
gender: Int, gender: Int,
profilePath: String? profilePath: String?
): Person(id, creditId, name, gender, profilePath) ): Person(id, name, gender, profilePath)

View File

@@ -0,0 +1,17 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class DetailCast(
@SerializedName("id") val id: Int,
@SerializedName("episode_count") val episodeCount: Int,
@SerializedName("overview") val overview: String,
@SerializedName("name") val name: String,
@SerializedName("media_type") val mediaType: String,
@SerializedName("poster_path") val posterPath: String?,
@SerializedName("first_air_date") val firstAirDate: String,
@SerializedName("character") val character: String,
@SerializedName("title") val title: String,
@SerializedName("adult") val isAdult: Boolean,
@SerializedName("release_date") val releaseDate: String
)

View File

@@ -0,0 +1,18 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class DetailCrew(
@SerializedName("id") val id: Int,
@SerializedName("department") val department: String,
@SerializedName("episode_count") val episodeCount: Int,
@SerializedName("job") val job: String,
@SerializedName("overview") val overview: String,
@SerializedName("name") val name: String,
@SerializedName("media_type") val mediaType: String,
@SerializedName("first_air_date") val firstAirDate: String,
@SerializedName("poster_path") val posterPath: String,
@SerializedName("title") val title: String,
@SerializedName("adult") val isAdult: Boolean,
@SerializedName("release_date") val releaseDate: String
)

View File

@@ -0,0 +1,16 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class DetailPerson(
@SerializedName("birthday") val birthday: String,
@SerializedName("known_for_department") val knownFor: String,
@SerializedName("deathday") val dateOfDeath: String?,
@SerializedName("biography") val biography: String,
@SerializedName("place_of_birth") val birthplace: String?,
@SerializedName("adult") val isAdult: Boolean,
id: Int,
name: String,
gender: Int,
profilePath: String?
): Person(id, name, gender, profilePath)

View File

@@ -4,7 +4,6 @@ import com.google.gson.annotations.SerializedName
open class Person( open class Person(
@SerializedName("id") val id: Int, @SerializedName("id") val id: Int,
@SerializedName("credit_id") val creditId: String,
@SerializedName("name") val name: String, @SerializedName("name") val name: String,
@SerializedName("gender") val gender: Int, @SerializedName("gender") val gender: Int,
@SerializedName("profile_path") val profilePath: String? @SerializedName("profile_path") val profilePath: String?

View File

@@ -0,0 +1,8 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class PersonCreditsResponse(
@SerializedName("cast") val cast: List<DetailCast>,
@SerializedName("crew") val crew: List<DetailCrew>
)

View File

@@ -0,0 +1,10 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class PersonImage(
@SerializedName("aspect_ratio") val aspectRatio: Float,
@SerializedName("file_path") val filePath: String,
@SerializedName("width") val width: Int,
@SerializedName("height") val height: Int
)

View File

@@ -0,0 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class PersonImageCollection(
@SerializedName("profiles") val images: List<PersonImage>
)

View File

@@ -3,7 +3,6 @@ package com.owenlejeune.tvtime.ui.components
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -13,14 +12,10 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
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.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter
import coil.transform.RoundedCornersTransformation
import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.extensions.dpToPx
@Composable @Composable
fun ContentCard( fun ContentCard(
@@ -106,24 +101,19 @@ fun ImageTextCard(
noDataImage: Int = R.drawable.placeholder, noDataImage: Int = R.drawable.placeholder,
placeholder: Int = R.drawable.placeholder, placeholder: Int = R.drawable.placeholder,
titleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, titleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
subtitleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant subtitleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
onItemClicked: () -> Unit = {}
) { ) {
val context = LocalContext.current Column(modifier = modifier) {
Column( PosterItem(
modifier = modifier width = 120.dp,
.padding(end = 12.dp) height = 180.dp,
) { onClick = onItemClicked,
Image( url = imageUrl,
modifier = Modifier noDataImage = noDataImage,
.size(width = 120.dp, height = 180.dp), placeholder = placeholder,
painter = rememberImagePainter( contentDescription = title,
data = imageUrl ?: noDataImage, elevation = 0.dp
builder = {
transformations(RoundedCornersTransformation(5f.dpToPx(context)))
placeholder(placeholder)
}
),
contentDescription = ""
) )
MinLinesText( MinLinesText(
modifier = Modifier modifier = Modifier

View File

@@ -31,6 +31,7 @@ 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.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
import com.owenlejeune.tvtime.api.tmdb.model.Person
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
@@ -54,6 +55,7 @@ fun PosterGrid(
) { ) {
listItems(mediaList.value) { item -> listItems(mediaList.value) { item ->
PosterItem( PosterItem(
modifier = Modifier.padding(5.dp),
mediaItem = item, mediaItem = item,
onClick = onClick onClick = onClick
) )
@@ -66,38 +68,86 @@ fun PosterItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
width: Dp = POSTER_WIDTH, width: Dp = POSTER_WIDTH,
height: Dp = POSTER_HEIGHT, height: Dp = POSTER_HEIGHT,
onClick: (Int) -> Unit = {}, onClick: (id: Int) -> Unit = {},
elevation: Dp = 8.dp,
mediaItem: TmdbItem? mediaItem: TmdbItem?
) { ) {
val context = LocalContext.current PosterItem(
val poster = mediaItem?.let { TmdbUtils.getFullPosterPath(mediaItem) } modifier = modifier,
Card( width = width,
height = height,
onClick = {
mediaItem?.let {
onClick(mediaItem.id)
}
},
url = mediaItem?.let { TmdbUtils.getFullPosterPath(mediaItem) },
elevation = elevation,
contentDescription = mediaItem?.title
)
}
@Composable
fun PosterItem(
modifier: Modifier = Modifier,
width: Dp = POSTER_WIDTH,
height: Dp = POSTER_HEIGHT,
onClick: (id: Int) -> Unit = {},
elevation: Dp = 8.dp,
person: Person?
) {
PosterItem(
modifier = modifier,
width = width,
height = height,
onClick = {
person?.let {
onClick(person.id)
}
},
url = person?.let { TmdbUtils.getFullPersonImagePath(person) },
elevation = 8.dp, elevation = 8.dp,
contentDescription = person?.name
)
}
@Composable
fun PosterItem(
url: String?,
modifier: Modifier = Modifier,
width: Dp = POSTER_WIDTH,
height: Dp = POSTER_HEIGHT,
onClick: () -> Unit = {},
noDataImage: Int = R.drawable.placeholder,
placeholder: Int = R.drawable.placeholder,
elevation: Dp = 8.dp,
contentDescription: String?
) {
val context = LocalContext.current
Card(
elevation = elevation,
modifier = modifier modifier = modifier
.size(width = width, height = height) .size(width = width, height = height),
.padding(5.dp),
shape = RoundedCornerShape(5.dp) shape = RoundedCornerShape(5.dp)
) { ) {
Image( Image(
painter = if (mediaItem != null) { painter = if (url != null) {
rememberImagePainter( rememberImagePainter(
data = poster, data = url ?: noDataImage,
builder = { builder = {
transformations(RoundedCornersTransformation(5f.dpToPx(context))) transformations(RoundedCornersTransformation(5f.dpToPx(context)))
placeholder(R.drawable.placeholder) placeholder(placeholder)
} }
) )
} else { } else {
rememberImagePainter(ContextCompat.getDrawable(context, R.drawable.placeholder)) rememberImagePainter(ContextCompat.getDrawable(context, R.drawable.placeholder))
}, },
contentDescription = mediaItem?.title, contentDescription = contentDescription,
modifier = Modifier modifier = Modifier
.size(width = width, height = height) .size(width = width, height = height)
.clickable { .clickable(
mediaItem?.let { onClick = onClick
onClick(mediaItem.id) )
}
}
) )
} }
} }

View File

@@ -11,6 +11,7 @@ import androidx.navigation.navArgument
import com.owenlejeune.tvtime.ui.screens.DetailView import com.owenlejeune.tvtime.ui.screens.DetailView
import com.owenlejeune.tvtime.ui.screens.MainAppView import com.owenlejeune.tvtime.ui.screens.MainAppView
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.MediaViewType
import com.owenlejeune.tvtime.ui.screens.PersonDetailView
import com.owenlejeune.tvtime.ui.screens.tabs.bottom.FavouritesTab import com.owenlejeune.tvtime.ui.screens.tabs.bottom.FavouritesTab
import com.owenlejeune.tvtime.ui.screens.tabs.bottom.MediaTab import com.owenlejeune.tvtime.ui.screens.tabs.bottom.MediaTab
import com.owenlejeune.tvtime.ui.screens.tabs.bottom.SettingsTab import com.owenlejeune.tvtime.ui.screens.tabs.bottom.SettingsTab
@@ -36,11 +37,19 @@ fun MainNavigationRoutes(navController: NavHostController, displayUnderStatusBar
) { navBackStackEntry -> ) { navBackStackEntry ->
displayUnderStatusBar.value = true displayUnderStatusBar.value = true
val args = navBackStackEntry.arguments val args = navBackStackEntry.arguments
DetailView( val mediaType = args?.getSerializable(NavConstants.TYPE_KEY) as MediaViewType
appNavController = navController, if (mediaType != MediaViewType.PERSON) {
itemId = args?.getInt(NavConstants.ID_KEY), DetailView(
type = args?.getSerializable(NavConstants.TYPE_KEY) as MediaViewType appNavController = navController,
) itemId = args.getInt(NavConstants.ID_KEY),
type = mediaType
)
} else {
PersonDetailView(
appNavController = navController,
personId = args.getInt(NavConstants.ID_KEY)
)
}
} }
} }
} }

View File

@@ -27,10 +27,12 @@ import androidx.navigation.NavController
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.PeopleService
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.listItems import com.owenlejeune.tvtime.extensions.listItems
import com.owenlejeune.tvtime.ui.components.* import com.owenlejeune.tvtime.ui.components.*
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
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
@@ -46,6 +48,7 @@ fun DetailView(
val service = when(type) { val service = when(type) {
MediaViewType.MOVIE -> MoviesService() MediaViewType.MOVIE -> MoviesService()
MediaViewType.TV -> TvService() MediaViewType.TV -> TvService()
else -> throw IllegalArgumentException("Media type given: ${type}, \n expected one of MediaViewType.MOVIE, MediaViewType.TV") // shouldn't happen
} }
val mediaItem = remember { mutableStateOf<DetailedItem?>(null) } val mediaItem = remember { mutableStateOf<DetailedItem?>(null) }
@@ -92,7 +95,8 @@ fun DetailView(
start.linkTo(posterImage.end, margin = 8.dp) start.linkTo(posterImage.end, margin = 8.dp)
end.linkTo(parent.end, margin = 16.dp) end.linkTo(parent.end, margin = 16.dp)
}, },
mediaItem = mediaItem title = mediaItem.value?.title ?: ""
// mediaItem = mediaItem
) )
BackButton( BackButton(
@@ -111,11 +115,100 @@ fun DetailView(
itemId = itemId, itemId = itemId,
mediaItem = mediaItem, mediaItem = mediaItem,
service = service, service = service,
mediaType = type mediaType = type,
appNavController = appNavController
) )
} }
} }
@Composable
fun PersonDetailView(
appNavController: NavController,
personId: Int?
) {
val person = remember { mutableStateOf<DetailPerson?>(null) }
personId?.let {
if (person.value == null) {
fetchPerson(personId, person)
}
}
val scrollState = rememberScrollState()
ConstraintLayout(
modifier = Modifier
.fillMaxSize()
.background(color = MaterialTheme.colorScheme.background)
.verticalScroll(state = scrollState)
) {
val (
backButton, backdropImage, profileImage, nameText, contentColumn
) = createRefs()
BackdropImage(
modifier = Modifier
.fillMaxWidth()
.height(280.dp)
.constrainAs(backdropImage) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
PosterItem(
person = person.value,
modifier = Modifier
.constrainAs(profileImage) {
bottom.linkTo(backdropImage.bottom)
start.linkTo(parent.start, margin = 16.dp)
top.linkTo(backButton.bottom)
}
)
TitleText(
modifier = Modifier.constrainAs(nameText) {
bottom.linkTo(profileImage.bottom)
start.linkTo(profileImage.end, margin = 8.dp)
end.linkTo(parent.end, margin = 16.dp)
},
title = person.value?.name ?: ""
)
BackButton(
modifier = Modifier.constrainAs(backButton) {
top.linkTo(parent.top)
start.linkTo(parent.start, 12.dp)
bottom.linkTo(profileImage.top)
},
appNavController = appNavController
)
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.constrainAs(contentColumn) {
top.linkTo(backdropImage.bottom)//, margin = 8.dp)
},
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
ContentCard {
Text(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = 12.dp, horizontal = 16.dp),
text = person.value?.biography ?: "",
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
@Composable @Composable
private fun Backdrop(modifier: Modifier, mediaItem: MutableState<DetailedItem?>) { private fun Backdrop(modifier: Modifier, mediaItem: MutableState<DetailedItem?>) {
// val images = remember { mutableStateOf<ImageCollection?>(null) } // val images = remember { mutableStateOf<ImageCollection?>(null) }
@@ -134,9 +227,9 @@ private fun Backdrop(modifier: Modifier, mediaItem: MutableState<DetailedItem?>)
} }
@Composable @Composable
private fun TitleText(modifier: Modifier, mediaItem: MutableState<DetailedItem?>) { private fun TitleText(modifier: Modifier, title: String/*mediaItem: MutableState<DetailedItem?>*/) {
Text( Text(
text = mediaItem.value?.title ?: "", text = title,//mediaItem.value?.title ?: "",
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
modifier = modifier modifier = modifier
.padding(start = 16.dp, end = 16.dp) .padding(start = 16.dp, end = 16.dp)
@@ -171,7 +264,8 @@ private fun ContentColumn(
itemId: Int?, itemId: Int?,
mediaItem: MutableState<DetailedItem?>, mediaItem: MutableState<DetailedItem?>,
service: DetailService, service: DetailService,
mediaType: MediaViewType mediaType: MediaViewType,
appNavController: NavController
) { ) {
Column( Column(
modifier = modifier modifier = modifier
@@ -190,7 +284,7 @@ private fun ContentColumn(
OverviewCard(mediaItem = mediaItem) OverviewCard(mediaItem = mediaItem)
} }
CastCard(itemId = itemId, service = service) CastCard(itemId = itemId, service = service, appNavController = appNavController)
SimilarContentCard(itemId = itemId, service = service) SimilarContentCard(itemId = itemId, service = service)
@@ -296,7 +390,7 @@ private fun OverviewCard(mediaItem: MutableState<DetailedItem?>, modifier: Modif
} }
@Composable @Composable
private fun CastCard(itemId: Int?, service: DetailService, modifier: Modifier = Modifier) { private fun CastCard(itemId: Int?, service: DetailService, appNavController: NavController, 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) {
@@ -311,19 +405,20 @@ private fun CastCard(itemId: Int?, service: DetailService, modifier: Modifier =
textColor = MaterialTheme.colorScheme.background textColor = MaterialTheme.colorScheme.background
) { ) {
LazyRow(modifier = Modifier LazyRow(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(12.dp) .padding(12.dp),
horizontalArrangement = Arrangement.spacedBy(4.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(appNavController = appNavController, person = castMember)
} }
} }
} }
} }
@Composable @Composable
private fun CastCrewCard(person: Person) { private fun CastCrewCard(appNavController: NavController, person: Person) {
ImageTextCard( ImageTextCard(
title = person.name, title = person.name,
modifier = Modifier modifier = Modifier
@@ -337,7 +432,12 @@ private fun CastCrewCard(person: Person) {
imageUrl = TmdbUtils.getFullPersonImagePath(person), imageUrl = TmdbUtils.getFullPersonImagePath(person),
noDataImage = R.drawable.no_person_photo, noDataImage = R.drawable.no_person_photo,
titleTextColor = MaterialTheme.colorScheme.onPrimary, titleTextColor = MaterialTheme.colorScheme.onPrimary,
subtitleTextColor = Color.Unspecified subtitleTextColor = Color.Unspecified,
onItemClicked = {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${MediaViewType.PERSON}/${person.id}"
)
}
) )
} }
@@ -512,4 +612,15 @@ private fun fetchVideos(id: Int, service: DetailService, videoResponse: MutableS
} }
} }
} }
}
private fun fetchPerson(id: Int, person: MutableState<DetailPerson?>) {
CoroutineScope(Dispatchers.IO).launch {
val result = PeopleService().getPerson(id)
if (result.isSuccessful) {
withContext(Dispatchers.Main) {
person.value = result.body()
}
}
}
} }

View File

@@ -2,5 +2,6 @@ package com.owenlejeune.tvtime.ui.screens
enum class MediaViewType { enum class MediaViewType {
MOVIE, MOVIE,
TV TV,
PERSON
} }

View File

@@ -27,6 +27,7 @@ fun MediaTab(appNavController: NavHostController, mediaType: MediaViewType) {
val tabs = when (mediaType) { val tabs = when (mediaType) {
MediaViewType.MOVIE -> MainTabNavItem.MovieItems MediaViewType.MOVIE -> MainTabNavItem.MovieItems
MediaViewType.TV -> MainTabNavItem.TvItems MediaViewType.TV -> MainTabNavItem.TvItems
else -> throw IllegalArgumentException("Media type given: ${mediaType}, \n expected one of MediaViewType.MOVIE, MediaViewType.TV") // shouldn't happen
} }
val pagerState = rememberPagerState() val pagerState = rememberPagerState()
Tabs(tabs = tabs, pagerState = pagerState) Tabs(tabs = tabs, pagerState = pagerState)
@@ -44,6 +45,7 @@ fun MediaTabContent(appNavController: NavHostController, mediaType: MediaViewTyp
val service: HomePageService = when(mediaType) { val service: HomePageService = when(mediaType) {
MediaViewType.MOVIE -> MoviesService() MediaViewType.MOVIE -> MoviesService()
MediaViewType.TV -> TvService() MediaViewType.TV -> TvService()
else -> throw IllegalArgumentException("Media type given: ${mediaType}, \n expected one of MediaViewType.MOVIE, MediaViewType.TV") // shouldn't happen
} }
PosterGrid( PosterGrid(
fetchMedia = { mediaList -> fetchMedia = { mediaList ->