mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-23 04:00:53 -05:00
show known for roles on person card
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.model
|
package com.owenlejeune.tvtime.api.tmdb.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.MediaViewType
|
||||||
|
|
||||||
class DetailCast(
|
class DetailCast(
|
||||||
@SerializedName("id") val id: Int,
|
@SerializedName("id") val id: Int,
|
||||||
@SerializedName("episode_count") val episodeCount: Int,
|
@SerializedName("episode_count") val episodeCount: Int,
|
||||||
@SerializedName("overview") val overview: String,
|
@SerializedName("overview") val overview: String,
|
||||||
@SerializedName("name") val name: String,
|
@SerializedName("name") val name: String,
|
||||||
@SerializedName("media_type") val mediaType: String,
|
@SerializedName("media_type") val mediaType: MediaViewType,
|
||||||
@SerializedName("poster_path") val posterPath: String?,
|
@SerializedName("poster_path") val posterPath: String?,
|
||||||
@SerializedName("first_air_date") val firstAirDate: String,
|
@SerializedName("first_air_date") val firstAirDate: String,
|
||||||
@SerializedName("character") val character: String,
|
@SerializedName("character") val character: String,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.model
|
package com.owenlejeune.tvtime.api.tmdb.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.MediaViewType
|
||||||
|
|
||||||
class DetailCrew(
|
class DetailCrew(
|
||||||
@SerializedName("id") val id: Int,
|
@SerializedName("id") val id: Int,
|
||||||
@@ -9,7 +10,7 @@ class DetailCrew(
|
|||||||
@SerializedName("job") val job: String,
|
@SerializedName("job") val job: String,
|
||||||
@SerializedName("overview") val overview: String,
|
@SerializedName("overview") val overview: String,
|
||||||
@SerializedName("name") val name: String,
|
@SerializedName("name") val name: String,
|
||||||
@SerializedName("media_type") val mediaType: String,
|
@SerializedName("media_type") val mediaType: MediaViewType,
|
||||||
@SerializedName("first_air_date") val firstAirDate: String,
|
@SerializedName("first_air_date") val firstAirDate: String,
|
||||||
@SerializedName("poster_path") val posterPath: String,
|
@SerializedName("poster_path") val posterPath: String,
|
||||||
@SerializedName("title") val title: String,
|
@SerializedName("title") val title: String,
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ fun ExpandableContentCard(
|
|||||||
color = toggleTextColor,
|
color = toggleTextColor,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
expandedState = !expandedState
|
expandedState = !expandedState
|
||||||
@@ -93,7 +93,7 @@ fun ExpandableContentCard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ImageTextCard(
|
fun TwoLineImageTextCard(
|
||||||
title: String,
|
title: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
subtitle: String? = null,
|
subtitle: String? = null,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.Brush
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
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.text.style.TextOverflow
|
||||||
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
|
||||||
@@ -96,7 +97,6 @@ fun DetailView(
|
|||||||
end.linkTo(parent.end, margin = 16.dp)
|
end.linkTo(parent.end, margin = 16.dp)
|
||||||
},
|
},
|
||||||
title = mediaItem.value?.title ?: ""
|
title = mediaItem.value?.title ?: ""
|
||||||
// mediaItem = mediaItem
|
|
||||||
)
|
)
|
||||||
|
|
||||||
BackButton(
|
BackButton(
|
||||||
@@ -194,17 +194,56 @@ fun PersonDetailView(
|
|||||||
},
|
},
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
ContentCard {
|
ExpandableContentCard { isExpanded ->
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(vertical = 12.dp, horizontal = 16.dp),
|
.padding(top = 12.dp, start = 16.dp, end = 16.dp),
|
||||||
text = person.value?.biography ?: "",
|
text = person.value?.biography ?: "",
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
maxLines = if (isExpanded) Int.MAX_VALUE else 3,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val credits = remember { mutableStateOf<PersonCreditsResponse?>(null) }
|
||||||
|
personId?.let {
|
||||||
|
if (credits.value == null) {
|
||||||
|
fetchCredits(personId, credits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentCard(title = stringResource(R.string.known_for_label)) {
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
items(credits.value?.cast?.size ?: 0) { i ->
|
||||||
|
val content = credits.value!!.cast[i]
|
||||||
|
|
||||||
|
TwoLineImageTextCard(
|
||||||
|
title = content.title,
|
||||||
|
subtitle = content.character,
|
||||||
|
modifier = Modifier
|
||||||
|
.width(124.dp)
|
||||||
|
.wrapContentHeight(),
|
||||||
|
imageUrl = TmdbUtils.getFullPosterPath(content.posterPath),
|
||||||
|
onItemClicked = {
|
||||||
|
personId?.let {
|
||||||
|
appNavController.navigate(
|
||||||
|
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,9 +266,9 @@ private fun Backdrop(modifier: Modifier, mediaItem: MutableState<DetailedItem?>)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TitleText(modifier: Modifier, title: String/*mediaItem: MutableState<DetailedItem?>*/) {
|
private fun TitleText(modifier: Modifier, title: String) {
|
||||||
Text(
|
Text(
|
||||||
text = title,//mediaItem.value?.title ?: "",
|
text = 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)
|
||||||
@@ -252,7 +291,7 @@ private fun BackButton(modifier: Modifier, appNavController: NavController) {
|
|||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
imageVector = Icons.Filled.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = stringResource(R.string.content_description_back_button),
|
||||||
tint = MaterialTheme.colorScheme.primary
|
tint = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -286,7 +325,7 @@ private fun ContentColumn(
|
|||||||
|
|
||||||
CastCard(itemId = itemId, service = service, appNavController = appNavController)
|
CastCard(itemId = itemId, service = service, appNavController = appNavController)
|
||||||
|
|
||||||
SimilarContentCard(itemId = itemId, service = service)
|
SimilarContentCard(itemId = itemId, service = service, mediaType = mediaType, appNavController = appNavController)
|
||||||
|
|
||||||
VideosCard(itemId = itemId, service = service)
|
VideosCard(itemId = itemId, service = service)
|
||||||
}
|
}
|
||||||
@@ -341,13 +380,14 @@ private fun MiscDetails(
|
|||||||
contentRating: MutableState<String>
|
contentRating: MutableState<String>
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(start = 8.dp, end = 10.dp, bottom = 8.dp)
|
.padding(horizontal = 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(text = year, color = MaterialTheme.colorScheme.onBackground)
|
Text(text = year, color = MaterialTheme.colorScheme.onBackground)
|
||||||
Text(
|
Text(
|
||||||
@@ -365,8 +405,7 @@ private fun MiscDetails(
|
|||||||
ChipGroup(
|
ChipGroup(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.wrapContentHeight(),
|
||||||
.padding(bottom = 8.dp),
|
|
||||||
chips = genres.map { it.name }
|
chips = genres.map { it.name }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -419,7 +458,7 @@ private fun CastCard(itemId: Int?, service: DetailService, appNavController: Nav
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CastCrewCard(appNavController: NavController, person: Person) {
|
private fun CastCrewCard(appNavController: NavController, person: Person) {
|
||||||
ImageTextCard(
|
TwoLineImageTextCard(
|
||||||
title = person.name,
|
title = person.name,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(124.dp)
|
.width(124.dp)
|
||||||
@@ -442,7 +481,13 @@ private fun CastCrewCard(appNavController: NavController, person: Person) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SimilarContentCard(itemId: Int?, service: DetailService, modifier: Modifier = Modifier) {
|
fun SimilarContentCard(
|
||||||
|
itemId: Int?,
|
||||||
|
service: DetailService,
|
||||||
|
mediaType: MediaViewType,
|
||||||
|
appNavController: NavController,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
val similarContent = remember { mutableStateOf<HomePageResponse?>(null) }
|
val similarContent = remember { mutableStateOf<HomePageResponse?>(null) }
|
||||||
itemId?.let {
|
itemId?.let {
|
||||||
if (similarContent.value == null) {
|
if (similarContent.value == null) {
|
||||||
@@ -457,17 +502,23 @@ fun SimilarContentCard(itemId: Int?, service: DetailService, modifier: Modifier
|
|||||||
LazyRow(modifier = Modifier
|
LazyRow(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(12.dp)
|
.padding(12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
) {
|
) {
|
||||||
items(similarContent.value?.results?.size ?: 0) { i ->
|
items(similarContent.value?.results?.size ?: 0) { i ->
|
||||||
val content = similarContent.value!!.results[i]
|
val content = similarContent.value!!.results[i]
|
||||||
|
|
||||||
ImageTextCard(
|
TwoLineImageTextCard(
|
||||||
title = content.title,
|
title = content.title,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(124.dp)
|
.width(124.dp)
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
imageUrl = TmdbUtils.getFullPosterPath(content)
|
imageUrl = TmdbUtils.getFullPosterPath(content),
|
||||||
|
onItemClicked = {
|
||||||
|
appNavController.navigate(
|
||||||
|
"${MainNavItem.DetailView.route}/${mediaType}/${content.id}"
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -492,7 +543,7 @@ fun VideosCard(itemId: Int?, service: DetailService, modifier: Modifier = Modifi
|
|||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.videos_label),
|
text = stringResource(id = R.string.videos_label),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
modifier = Modifier.padding(start = 12.dp, top = 8.dp),
|
modifier = Modifier.padding(start = 16.dp, top = 8.dp),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -516,19 +567,20 @@ private fun VideoGroup(results: List<Video>, type: Video.Type, title: String) {
|
|||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier.padding(start = 12.dp, top = 8.dp)
|
modifier = Modifier.padding(start = 16.dp, top = 8.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
val posterWidth = 120.dp
|
val posterWidth = 120.dp
|
||||||
LazyRow(modifier = Modifier
|
LazyRow(modifier = Modifier
|
||||||
.padding(horizontal = 8.dp, vertical = 8.dp)
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
) {
|
) {
|
||||||
listItems(videos) { video ->
|
listItems(videos) { video ->
|
||||||
FullScreenThumbnailVideoPlayer(
|
FullScreenThumbnailVideoPlayer(
|
||||||
key = video.key,
|
key = video.key,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(posterWidth)
|
.width(posterWidth)
|
||||||
.height(80.dp)
|
.height(90.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -624,3 +676,14 @@ private fun fetchPerson(id: Int, person: MutableState<DetailPerson?>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fetchCredits(id: Int, credits: MutableState<PersonCreditsResponse?>) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val result = PeopleService().getCredits(id)
|
||||||
|
if (result.isSuccessful) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
credits.value = result.body()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,12 +15,14 @@ import androidx.compose.ui.focus.onFocusChanged
|
|||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.components.RoundedTextField
|
import com.owenlejeune.tvtime.ui.components.RoundedTextField
|
||||||
import com.owenlejeune.tvtime.ui.components.SearchFab
|
import com.owenlejeune.tvtime.ui.components.SearchFab
|
||||||
@@ -137,7 +139,7 @@ private fun SearchTopBar(
|
|||||||
focusRequester = focusRequester,
|
focusRequester = focusRequester,
|
||||||
value = textState,
|
value = textState,
|
||||||
onValueChange = { textState = it },
|
onValueChange = { textState = it },
|
||||||
placeHolder = "Search ${title.value}"
|
placeHolder = stringResource(id = R.string.search_placeholder, title.value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
enum class MediaViewType {
|
enum class MediaViewType {
|
||||||
|
@SerializedName("movie")
|
||||||
MOVIE,
|
MOVIE,
|
||||||
|
@SerializedName("tv")
|
||||||
TV,
|
TV,
|
||||||
PERSON
|
PERSON
|
||||||
}
|
}
|
||||||
@@ -17,10 +17,13 @@
|
|||||||
<string name="cast_label">Cast</string>
|
<string name="cast_label">Cast</string>
|
||||||
<string name="recommended_label">Recommended</string>
|
<string name="recommended_label">Recommended</string>
|
||||||
<string name="videos_label">Videos</string>
|
<string name="videos_label">Videos</string>
|
||||||
|
<string name="known_for_label">Known For</string>
|
||||||
|
|
||||||
<string name="expandable_see_more">See more</string>
|
<string name="expandable_see_more">See more</string>
|
||||||
<string name="expandable_see_less">See less</string>
|
<string name="expandable_see_less">See less</string>
|
||||||
|
|
||||||
|
<string name="search_placeholder">Search %1$s</string>
|
||||||
|
|
||||||
<!-- preferences -->
|
<!-- preferences -->
|
||||||
<string name="preference_heading_search">Search</string>
|
<string name="preference_heading_search">Search</string>
|
||||||
<string name="preferences_persistent_search_title">Persistent search bar</string>
|
<string name="preferences_persistent_search_title">Persistent search bar</string>
|
||||||
@@ -35,4 +38,5 @@
|
|||||||
<string name="video_type_teaser">Teasers</string>
|
<string name="video_type_teaser">Teasers</string>
|
||||||
<string name="video_type_behind_the_scenes">Behind the Scenes</string>
|
<string name="video_type_behind_the_scenes">Behind the Scenes</string>
|
||||||
<string name="video_type_featureette">Featurettes</string>
|
<string name="video_type_featureette">Featurettes</string>
|
||||||
|
<string name="content_description_back_button">Back</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user