diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/PeopleApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/PeopleApi.kt new file mode 100644 index 0000000..c1cb652 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/PeopleApi.kt @@ -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 + +// @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 + + @GET("person/{id}/images") + suspend fun getImages(@Path("id") id: Int): Response + +// @GET("persons/{id}/tagged_images") +// suspend fun getTaggedImages(@Path("id") id: Int, @Query("page") page: Int = 1): Response + +} \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/PeopleService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/PeopleService.kt new file mode 100644 index 0000000..3e73b0c --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/PeopleService.kt @@ -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 { + return service.getPerson(id) + } + + suspend fun getCredits(id: Int): Response { + return service.getCredits(id) + } + + suspend fun getImages(id: Int): Response { + return service.getImages(id) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt index 876603e..d5af08b 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt @@ -33,6 +33,10 @@ class TmdbClient: KoinComponent { return client.create(TvApi::class.java) } + fun createPeopleService(): PeopleApi { + return client.create(PeopleApi::class.java) + } + private inner class TmdbInterceptor: Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CastMember.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CastMember.kt index 77b9240..f449dbc 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CastMember.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CastMember.kt @@ -5,9 +5,9 @@ import com.google.gson.annotations.SerializedName class CastMember( @SerializedName("character") val character: String, @SerializedName("order") val order: Int, + @SerializedName("credit_id") val creditId: String, id: Int, - creditId: String, name: String, gender: Int, profilePath: String? -): Person(id, creditId, name, gender, profilePath) \ No newline at end of file +): Person(id, name, gender, profilePath) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CrewMember.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CrewMember.kt index d826e33..6b2aaae 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CrewMember.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/CrewMember.kt @@ -5,9 +5,9 @@ import com.google.gson.annotations.SerializedName class CrewMember( @SerializedName("department") val department: String, @SerializedName("job") val job: String, + @SerializedName("credit_id") val creditId: String, id: Int, - creditId: String, name: String, gender: Int, profilePath: String? -): Person(id, creditId, name, gender, profilePath) +): Person(id, name, gender, profilePath) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailCast.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailCast.kt new file mode 100644 index 0000000..8996698 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailCast.kt @@ -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 +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailCrew.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailCrew.kt new file mode 100644 index 0000000..f9eeb3e --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailCrew.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailPerson.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailPerson.kt new file mode 100644 index 0000000..2b87a4d --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/DetailPerson.kt @@ -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) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Person.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Person.kt index 3fab30f..7544d2c 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Person.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Person.kt @@ -4,7 +4,6 @@ import com.google.gson.annotations.SerializedName open class Person( @SerializedName("id") val id: Int, - @SerializedName("credit_id") val creditId: String, @SerializedName("name") val name: String, @SerializedName("gender") val gender: Int, @SerializedName("profile_path") val profilePath: String? diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonCreditsResponse.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonCreditsResponse.kt new file mode 100644 index 0000000..ca58427 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonCreditsResponse.kt @@ -0,0 +1,8 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class PersonCreditsResponse( + @SerializedName("cast") val cast: List, + @SerializedName("crew") val crew: List +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonImage.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonImage.kt new file mode 100644 index 0000000..f719e15 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonImage.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonImageCollection.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonImageCollection.kt new file mode 100644 index 0000000..c5e4038 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/PersonImageCollection.kt @@ -0,0 +1,7 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class PersonImageCollection( + @SerializedName("profiles") val images: List +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Cards.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Cards.kt index fa8a050..136a366 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Cards.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Cards.kt @@ -3,7 +3,6 @@ package com.owenlejeune.tvtime.ui.components import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.tween -import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape @@ -13,14 +12,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import coil.compose.rememberImagePainter -import coil.transform.RoundedCornersTransformation import com.owenlejeune.tvtime.R -import com.owenlejeune.tvtime.extensions.dpToPx @Composable fun ContentCard( @@ -106,24 +101,19 @@ fun ImageTextCard( noDataImage: Int = R.drawable.placeholder, placeholder: Int = R.drawable.placeholder, titleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, - subtitleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant + subtitleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, + onItemClicked: () -> Unit = {} ) { - 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 = "" + Column(modifier = modifier) { + PosterItem( + width = 120.dp, + height = 180.dp, + onClick = onItemClicked, + url = imageUrl, + noDataImage = noDataImage, + placeholder = placeholder, + contentDescription = title, + elevation = 0.dp ) MinLinesText( modifier = Modifier diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt index 6aed1be..3ba4fae 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt @@ -31,6 +31,7 @@ import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.rememberPagerState import com.owenlejeune.tvtime.R 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.extensions.dpToPx import com.owenlejeune.tvtime.extensions.listItems @@ -54,6 +55,7 @@ fun PosterGrid( ) { listItems(mediaList.value) { item -> PosterItem( + modifier = Modifier.padding(5.dp), mediaItem = item, onClick = onClick ) @@ -66,38 +68,86 @@ fun PosterItem( modifier: Modifier = Modifier, width: Dp = POSTER_WIDTH, height: Dp = POSTER_HEIGHT, - onClick: (Int) -> Unit = {}, + onClick: (id: Int) -> Unit = {}, + elevation: Dp = 8.dp, mediaItem: TmdbItem? ) { - val context = LocalContext.current - val poster = mediaItem?.let { TmdbUtils.getFullPosterPath(mediaItem) } - Card( + PosterItem( + modifier = modifier, + 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, + 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 - .size(width = width, height = height) - .padding(5.dp), + .size(width = width, height = height), shape = RoundedCornerShape(5.dp) ) { Image( - painter = if (mediaItem != null) { + painter = if (url != null) { rememberImagePainter( - data = poster, + data = url ?: noDataImage, builder = { transformations(RoundedCornersTransformation(5f.dpToPx(context))) - placeholder(R.drawable.placeholder) + placeholder(placeholder) } ) } else { rememberImagePainter(ContextCompat.getDrawable(context, R.drawable.placeholder)) }, - contentDescription = mediaItem?.title, + contentDescription = contentDescription, modifier = Modifier .size(width = width, height = height) - .clickable { - mediaItem?.let { - onClick(mediaItem.id) - } - } + .clickable( + onClick = onClick + ) ) } } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt index 0b86f5a..fe17b36 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt @@ -11,6 +11,7 @@ import androidx.navigation.navArgument import com.owenlejeune.tvtime.ui.screens.DetailView import com.owenlejeune.tvtime.ui.screens.MainAppView 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.MediaTab import com.owenlejeune.tvtime.ui.screens.tabs.bottom.SettingsTab @@ -36,11 +37,19 @@ fun MainNavigationRoutes(navController: NavHostController, displayUnderStatusBar ) { navBackStackEntry -> displayUnderStatusBar.value = true val args = navBackStackEntry.arguments - DetailView( - appNavController = navController, - itemId = args?.getInt(NavConstants.ID_KEY), - type = args?.getSerializable(NavConstants.TYPE_KEY) as MediaViewType - ) + val mediaType = args?.getSerializable(NavConstants.TYPE_KEY) as MediaViewType + if (mediaType != MediaViewType.PERSON) { + DetailView( + appNavController = navController, + itemId = args.getInt(NavConstants.ID_KEY), + type = mediaType + ) + } else { + PersonDetailView( + appNavController = navController, + personId = args.getInt(NavConstants.ID_KEY) + ) + } } } } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt index 9903dd4..49b97ad 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt @@ -27,10 +27,12 @@ import androidx.navigation.NavController import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.DetailService 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.model.* import com.owenlejeune.tvtime.extensions.listItems import com.owenlejeune.tvtime.ui.components.* +import com.owenlejeune.tvtime.ui.navigation.MainNavItem import com.owenlejeune.tvtime.utils.TmdbUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -46,6 +48,7 @@ fun DetailView( val service = when(type) { MediaViewType.MOVIE -> MoviesService() 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(null) } @@ -92,7 +95,8 @@ fun DetailView( start.linkTo(posterImage.end, margin = 8.dp) end.linkTo(parent.end, margin = 16.dp) }, - mediaItem = mediaItem + title = mediaItem.value?.title ?: "" +// mediaItem = mediaItem ) BackButton( @@ -111,11 +115,100 @@ fun DetailView( itemId = itemId, mediaItem = mediaItem, service = service, - mediaType = type + mediaType = type, + appNavController = appNavController ) } } +@Composable +fun PersonDetailView( + appNavController: NavController, + personId: Int? +) { + val person = remember { mutableStateOf(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 private fun Backdrop(modifier: Modifier, mediaItem: MutableState) { // val images = remember { mutableStateOf(null) } @@ -134,9 +227,9 @@ private fun Backdrop(modifier: Modifier, mediaItem: MutableState) } @Composable -private fun TitleText(modifier: Modifier, mediaItem: MutableState) { +private fun TitleText(modifier: Modifier, title: String/*mediaItem: MutableState*/) { Text( - text = mediaItem.value?.title ?: "", + text = title,//mediaItem.value?.title ?: "", color = MaterialTheme.colorScheme.primary, modifier = modifier .padding(start = 16.dp, end = 16.dp) @@ -171,7 +264,8 @@ private fun ContentColumn( itemId: Int?, mediaItem: MutableState, service: DetailService, - mediaType: MediaViewType + mediaType: MediaViewType, + appNavController: NavController ) { Column( modifier = modifier @@ -190,7 +284,7 @@ private fun ContentColumn( OverviewCard(mediaItem = mediaItem) } - CastCard(itemId = itemId, service = service) + CastCard(itemId = itemId, service = service, appNavController = appNavController) SimilarContentCard(itemId = itemId, service = service) @@ -296,7 +390,7 @@ private fun OverviewCard(mediaItem: MutableState, modifier: Modif } @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(null) } itemId?.let { if (castAndCrew.value == null) { @@ -311,19 +405,20 @@ private fun CastCard(itemId: Int?, service: DetailService, modifier: Modifier = textColor = MaterialTheme.colorScheme.background ) { LazyRow(modifier = Modifier - .fillMaxWidth() - .padding(12.dp) + .fillMaxWidth() + .padding(12.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp) ) { items(castAndCrew.value?.cast?.size ?: 0) { i -> val castMember = castAndCrew.value!!.cast[i] - CastCrewCard(person = castMember) + CastCrewCard(appNavController = appNavController, person = castMember) } } } } @Composable -private fun CastCrewCard(person: Person) { +private fun CastCrewCard(appNavController: NavController, person: Person) { ImageTextCard( title = person.name, modifier = Modifier @@ -337,7 +432,12 @@ private fun CastCrewCard(person: Person) { imageUrl = TmdbUtils.getFullPersonImagePath(person), noDataImage = R.drawable.no_person_photo, 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) { + CoroutineScope(Dispatchers.IO).launch { + val result = PeopleService().getPerson(id) + if (result.isSuccessful) { + withContext(Dispatchers.Main) { + person.value = result.body() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/MediaViewType.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/MediaViewType.kt index 6020758..612df97 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/MediaViewType.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/MediaViewType.kt @@ -2,5 +2,6 @@ package com.owenlejeune.tvtime.ui.screens enum class MediaViewType { MOVIE, - TV + TV, + PERSON } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/MediaTab.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/MediaTab.kt index 60c3133..f6c2d31 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/MediaTab.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/MediaTab.kt @@ -27,6 +27,7 @@ fun MediaTab(appNavController: NavHostController, mediaType: MediaViewType) { val tabs = when (mediaType) { MediaViewType.MOVIE -> MainTabNavItem.MovieItems 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() Tabs(tabs = tabs, pagerState = pagerState) @@ -44,6 +45,7 @@ fun MediaTabContent(appNavController: NavHostController, mediaType: MediaViewTyp val service: HomePageService = when(mediaType) { MediaViewType.MOVIE -> MoviesService() MediaViewType.TV -> TvService() + else -> throw IllegalArgumentException("Media type given: ${mediaType}, \n expected one of MediaViewType.MOVIE, MediaViewType.TV") // shouldn't happen } PosterGrid( fetchMedia = { mediaList ->