display cast and overview on details screen

This commit is contained in:
Owen LeJeune
2022-02-15 17:18:52 -05:00
parent 86034bf198
commit cbb44513b6
17 changed files with 276 additions and 61 deletions

View File

@@ -15,8 +15,8 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:theme="@style/Theme.TVTime"
android:theme="@style/Theme.TVTime"> android:screenOrientation="portrait">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@@ -1,28 +1,20 @@
package com.owenlejeune.tvtime package com.owenlejeune.tvtime
import android.os.Bundle import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme
import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable
import androidx.compose.material.icons.Icons import androidx.compose.runtime.MutableState
import androidx.compose.material.icons.filled.Search import androidx.compose.runtime.mutableStateOf
import androidx.compose.material3.* import androidx.compose.runtime.remember
import androidx.compose.runtime.* import androidx.compose.ui.graphics.Color
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.owenlejeune.tvtime.ui.navigation.BottomNavItem import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.ui.navigation.BottomNavigationRoutes
import com.owenlejeune.tvtime.ui.navigation.MainNavigationRoutes import com.owenlejeune.tvtime.ui.navigation.MainNavigationRoutes
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
@@ -31,7 +23,14 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
val displayUnderStatusBar = remember { mutableStateOf(false) } val displayUnderStatusBar = remember { mutableStateOf(false) }
WindowCompat.setDecorFitsSystemWindows(window, !displayUnderStatusBar.value) // WindowCompat.setDecorFitsSystemWindows(window, !displayUnderStatusBar.value)
// val statusBarColor = if (displayUnderStatusBar.value) {
// Color.Transparent
// } else {
// MaterialTheme.colorScheme.background
// }
// val systemUiController = rememberSystemUiController()
// systemUiController.setStatusBarColor(statusBarColor, !isSystemInDarkTheme())
MyApp(displayUnderStatusBar = displayUnderStatusBar) MyApp(displayUnderStatusBar = displayUnderStatusBar)
} }
} }

View File

@@ -1,5 +1,6 @@
package com.owenlejeune.tvtime.api.tmdb package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
import com.owenlejeune.tvtime.api.tmdb.model.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 retrofit2.Response import retrofit2.Response
@@ -10,4 +11,6 @@ interface DetailService {
suspend fun getImages(id: Int): Response<ImageCollection> suspend fun getImages(id: Int): Response<ImageCollection>
suspend fun getCastAndCrew(id: Int): Response<CastAndCrew>
} }

View File

@@ -1,5 +1,6 @@
package com.owenlejeune.tvtime.api.tmdb package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
import com.owenlejeune.tvtime.api.tmdb.model.DetailedMovie import com.owenlejeune.tvtime.api.tmdb.model.DetailedMovie
import com.owenlejeune.tvtime.api.tmdb.model.PopularMoviesResponse import com.owenlejeune.tvtime.api.tmdb.model.PopularMoviesResponse
@@ -19,4 +20,7 @@ interface MoviesApi {
@GET("movie/{id}/images") @GET("movie/{id}/images")
suspend fun getMovieImages(@Path("id") id: Int): Response<ImageCollection> suspend fun getMovieImages(@Path("id") id: Int): Response<ImageCollection>
@GET("movie/{id}/credits")
suspend fun getCastAndCrew(@Path("id") id: Int): Response<CastAndCrew>
} }

View File

@@ -1,5 +1,6 @@
package com.owenlejeune.tvtime.api.tmdb package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
import com.owenlejeune.tvtime.api.tmdb.model.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 org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
@@ -19,4 +20,8 @@ class MoviesService: KoinComponent, DetailService {
return service.getMovieImages(id) return service.getMovieImages(id)
} }
override suspend fun getCastAndCrew(id: Int): Response<CastAndCrew> {
return service.getCastAndCrew(id)
}
} }

View File

@@ -2,10 +2,15 @@ package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem
import com.owenlejeune.tvtime.api.tmdb.model.Image import com.owenlejeune.tvtime.api.tmdb.model.Image
import com.owenlejeune.tvtime.api.tmdb.model.Person
import com.owenlejeune.tvtime.api.tmdb.model.TmdbItem import com.owenlejeune.tvtime.api.tmdb.model.TmdbItem
object TmdbUtils { object TmdbUtils {
private const val POSTER_BASE = "https://image.tmdb.org/t/p/original"
private const val BACKDROP_BASE = "https://www.themoviedb.org/t/p/original"
private const val PERSON_BASE = "https://www.themoviedb.org/t/p/w600_and_h900_bestv2"
fun getFullPosterPath(posterPath: String?): String? { fun getFullPosterPath(posterPath: String?): String? {
return posterPath?.let { "https://image.tmdb.org/t/p/original${posterPath}" } return posterPath?.let { "https://image.tmdb.org/t/p/original${posterPath}" }
} }
@@ -30,4 +35,12 @@ object TmdbUtils {
return getFullBackdropPath(image.filePath) return getFullBackdropPath(image.filePath)
} }
fun getFullPersonImagePath(path: String?): String? {
return path?.let { "https://www.themoviedb.org/t/p/w600_and_h900_bestv2${path}" }
}
fun getFullPersonImagePath(person: Person): String? {
return getFullPersonImagePath(person.profilePath)
}
} }

View File

@@ -1,5 +1,6 @@
package com.owenlejeune.tvtime.api.tmdb package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
import com.owenlejeune.tvtime.api.tmdb.model.PopularTvResponse import com.owenlejeune.tvtime.api.tmdb.model.PopularTvResponse
import com.owenlejeune.tvtime.api.tmdb.model.DetailedTv import com.owenlejeune.tvtime.api.tmdb.model.DetailedTv
@@ -19,4 +20,7 @@ interface TvApi {
@GET("tv/{id}/images") @GET("tv/{id}/images")
suspend fun getTvImages(@Path("id") id: Int): Response<ImageCollection> suspend fun getTvImages(@Path("id") id: Int): Response<ImageCollection>
@GET("tv/{id}/credits")
suspend fun getCastAndCrew(@Path("id") id: Int): Response<CastAndCrew>
} }

View File

@@ -1,5 +1,6 @@
package com.owenlejeune.tvtime.api.tmdb package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
import com.owenlejeune.tvtime.api.tmdb.model.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 org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
@@ -18,4 +19,8 @@ class TvService: KoinComponent, DetailService {
override suspend fun getImages(id: Int): Response<ImageCollection> { override suspend fun getImages(id: Int): Response<ImageCollection> {
return service.getTvImages(id) return service.getTvImages(id)
} }
override suspend fun getCastAndCrew(id: Int): Response<CastAndCrew> {
return service.getCastAndCrew(id)
}
} }

View File

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

View File

@@ -0,0 +1,13 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class CastMember(
@SerializedName("character") val character: String,
@SerializedName("order") val order: Int,
id: Int,
creditId: String,
name: String,
gender: Int,
profilePath: String?
): Person(id, creditId, name, gender, profilePath)

View File

@@ -0,0 +1,13 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class CrewMember(
@SerializedName("department") val department: String,
@SerializedName("job") val job: String,
id: Int,
creditId: String,
name: String,
gender: Int,
profilePath: String?
): Person(id, creditId, name, gender, profilePath)

View File

@@ -4,7 +4,8 @@ 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: 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?
) )

View File

@@ -11,6 +11,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.GridCells import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -67,28 +69,35 @@ fun PosterItem(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val poster = mediaItem?.let { TmdbUtils.getFullPosterPath(mediaItem) } val poster = mediaItem?.let { TmdbUtils.getFullPosterPath(mediaItem) }
Image( Card(
painter = if (mediaItem != null) { elevation = 8.dp,
rememberImagePainter(
data = poster,
builder = {
transformations(RoundedCornersTransformation(5f.dpToPx(context)))
placeholder(R.drawable.placeholder)
}
)
} else {
rememberImagePainter(ContextCompat.getDrawable(context, R.drawable.placeholder))
},
contentDescription = mediaItem?.title,
modifier = modifier modifier = modifier
.size(width = width, height = height) .size(width = width, height = height)
.padding(5.dp) .padding(5.dp),
.clickable { shape = RoundedCornerShape(5.dp)
mediaItem?.let { ) {
onClick(mediaItem.id) Image(
painter = if (mediaItem != null) {
rememberImagePainter(
data = poster,
builder = {
transformations(RoundedCornersTransformation(5f.dpToPx(context)))
placeholder(R.drawable.placeholder)
}
)
} else {
rememberImagePainter(ContextCompat.getDrawable(context, R.drawable.placeholder))
},
contentDescription = mediaItem?.title,
modifier = Modifier
.size(width = width, height = height)
.clickable {
mediaItem?.let {
onClick(mediaItem.id)
}
} }
} )
) }
} }
@SuppressLint("CoroutineCreationDuringComposition") @SuppressLint("CoroutineCreationDuringComposition")

View File

@@ -26,8 +26,6 @@ fun MainNavigationRoutes(navController: NavHostController, displayUnderStatusBar
NavHost(navController = navController, startDestination = MainNavItem.MainView.route) { NavHost(navController = navController, startDestination = MainNavItem.MainView.route) {
composable(MainNavItem.MainView.route) { composable(MainNavItem.MainView.route) {
displayUnderStatusBar.value = false displayUnderStatusBar.value = false
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(MaterialTheme.colorScheme.background, !isSystemInDarkTheme())
MainAppView(appNavController = navController) MainAppView(appNavController = navController)
} }
composable( composable(
@@ -38,8 +36,6 @@ fun MainNavigationRoutes(navController: NavHostController, displayUnderStatusBar
) )
) { navBackStackEntry -> ) { navBackStackEntry ->
displayUnderStatusBar.value = true displayUnderStatusBar.value = true
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(Color.Transparent, !isSystemInDarkTheme())
val args = navBackStackEntry.arguments val args = navBackStackEntry.arguments
DetailView( DetailView(
appNavController = navController, appNavController = navController,

View File

@@ -1,7 +1,13 @@
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.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
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
@@ -16,17 +22,23 @@ 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.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
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.compose.ui.unit.sp
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.api.tmdb.DetailService import com.owenlejeune.tvtime.api.tmdb.DetailService
import com.owenlejeune.tvtime.api.tmdb.MoviesService import com.owenlejeune.tvtime.api.tmdb.MoviesService
import com.owenlejeune.tvtime.api.tmdb.TmdbUtils import com.owenlejeune.tvtime.api.tmdb.TmdbUtils
import com.owenlejeune.tvtime.api.tmdb.TvService import com.owenlejeune.tvtime.api.tmdb.TvService
import com.owenlejeune.tvtime.api.tmdb.model.CastAndCrew
import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem
import com.owenlejeune.tvtime.api.tmdb.model.Image
import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection
import com.owenlejeune.tvtime.extensions.dpToPx
import com.owenlejeune.tvtime.ui.components.BackdropImage import com.owenlejeune.tvtime.ui.components.BackdropImage
import com.owenlejeune.tvtime.ui.components.PosterItem import com.owenlejeune.tvtime.ui.components.PosterItem
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -48,24 +60,32 @@ fun DetailView(
val mediaItem = remember { mutableStateOf<DetailedItem?>(null) } val mediaItem = remember { mutableStateOf<DetailedItem?>(null) }
itemId?.let { itemId?.let {
fetchMediaItem(itemId, service, mediaItem) if (mediaItem.value == null) {
fetchMediaItem(itemId, service, mediaItem)
}
} }
val images = remember { mutableStateOf<ImageCollection?>(null) } val images = remember { mutableStateOf<ImageCollection?>(null) }
itemId?.let { itemId?.let {
fetchImages(itemId, service, images) if (images.value == null) {
fetchImages(itemId, service, images)
}
} }
val scrollState = rememberScrollState()
ConstraintLayout( ConstraintLayout(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(color = MaterialTheme.colorScheme.background) .background(color = MaterialTheme.colorScheme.background)
.verticalScroll(state = scrollState)
) { ) {
val ( val (
backButton, backButton,
backdropImage, backdropImage,
posterImage, posterImage,
title titleText,
contentColumn
) = createRefs() ) = createRefs()
BackdropImage( BackdropImage(
@@ -75,18 +95,18 @@ fun DetailView(
start.linkTo(parent.start) start.linkTo(parent.start)
} }
.fillMaxWidth() .fillMaxWidth()
.size(0.dp, 280.dp), .height(280.dp),
imageUrl = TmdbUtils.getFullBackdropPath(mediaItem.value), imageUrl = TmdbUtils.getFullBackdropPath(mediaItem.value),
collection = images.value // collection = images.value
) )
PosterItem( PosterItem(
mediaItem = mediaItem.value, mediaItem = mediaItem.value,
modifier = Modifier modifier = Modifier
.constrainAs(posterImage) { .constrainAs(posterImage) {
bottom.linkTo(title.top, margin = 8.dp) bottom.linkTo(backdropImage.bottom)
start.linkTo(parent.start, margin = 16.dp) start.linkTo(parent.start, margin = 16.dp)
top.linkTo(backButton.bottom, margin = 8.dp) top.linkTo(backButton.bottom)
} }
) )
@@ -94,14 +114,14 @@ fun DetailView(
text = mediaItem.value?.title ?: "", text = mediaItem.value?.title ?: "",
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
modifier = Modifier modifier = Modifier
.constrainAs(title) { .constrainAs(titleText) {
bottom.linkTo(backdropImage.bottom, margin = 8.dp) bottom.linkTo(posterImage.bottom)
start.linkTo(parent.start, margin = 20.dp) start.linkTo(posterImage.end, margin = 8.dp)
end.linkTo(parent.end, margin = 16.dp) end.linkTo(parent.end, margin = 16.dp)
} }
.padding(start = 16.dp, end = 16.dp) .padding(start = 16.dp, end = 16.dp)
.fillMaxWidth(), .fillMaxWidth(.6f),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Start, textAlign = TextAlign.Start,
softWrap = true softWrap = true
) )
@@ -110,10 +130,18 @@ fun DetailView(
onClick = { appNavController.popBackStack() }, onClick = { appNavController.popBackStack() },
modifier = Modifier modifier = Modifier
.constrainAs(backButton) { .constrainAs(backButton) {
top.linkTo(parent.top, 16.dp) top.linkTo(parent.top)//, 8.dp)
start.linkTo(parent.start, 12.dp) start.linkTo(parent.start, 12.dp)
bottom.linkTo(posterImage.top)
} }
.background(brush = Brush.radialGradient(colors = listOf(Color.Black, Color.Transparent))) .background(
brush = Brush.radialGradient(
colors = listOf(
Color.Black,
Color.Transparent
)
)
)
.wrapContentSize() .wrapContentSize()
) { ) {
Icon( Icon(
@@ -122,6 +150,108 @@ fun DetailView(
tint = MaterialTheme.colorScheme.primary tint = MaterialTheme.colorScheme.primary
) )
} }
val castAndCrew = remember { mutableStateOf<CastAndCrew?>(null) }
itemId?.let {
if (castAndCrew.value == null) {
fetchCastAndCrew(itemId, service, castAndCrew)
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(horizontal = 16.dp)
.constrainAs(contentColumn) {
top.linkTo(backdropImage.bottom, margin = 8.dp)
}
) {
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(bottom = 12.dp),
shape = RoundedCornerShape(10.dp),
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
elevation = 8.dp
) {
Text(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = 12.dp, horizontal = 16.dp),
text = mediaItem.value?.overview ?: "",
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodyMedium
)
}
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
shape = RoundedCornerShape(10.dp),
backgroundColor = MaterialTheme.colorScheme.primary,
elevation = 8.dp
) {
LazyRow(modifier = Modifier
.fillMaxSize()
.padding(12.dp)
) {
items(castAndCrew.value?.cast?.size ?: 0) { i ->
val castMember = castAndCrew.value!!.cast[i]
Column(
modifier = Modifier
.width(124.dp)
.wrapContentHeight()
.padding(end = 12.dp)
) {
Image(
modifier = Modifier
.size(width = 120.dp, height = 180.dp),
painter = rememberImagePainter(
data = TmdbUtils.getFullPersonImagePath(castMember),
builder = {
transformations(RoundedCornersTransformation(5f.dpToPx(context)))
placeholder(R.drawable.placeholder)
}
),
contentDescription = ""
)
val nameLineHeight = MaterialTheme.typography.bodyMedium.fontSize*4/3
Text(
modifier = Modifier
.fillMaxWidth()
.padding(top = 5.dp)
.sizeIn(
minHeight = with(LocalDensity.current) {
(nameLineHeight * 2).toDp()
}
),
text = castMember.name,
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.bodyMedium,
lineHeight = nameLineHeight
)
val characterLineHeight = MaterialTheme.typography.bodySmall.fontSize*4/3
Text(
modifier = Modifier
.fillMaxWidth()
.sizeIn(
minHeight = with(LocalDensity.current) {
(characterLineHeight * 2).toDp()
}
),
text = castMember.character,
style = MaterialTheme.typography.bodySmall,
lineHeight = characterLineHeight
)
}
}
}
}
}
} }
} }
@@ -147,6 +277,17 @@ private fun fetchImages(id: Int, service: DetailService, images: MutableState<Im
} }
} }
private fun fetchCastAndCrew(id: Int, service: DetailService, castAndCrew: MutableState<CastAndCrew?>) {
CoroutineScope(Dispatchers.IO).launch {
val response = service.getCastAndCrew(id)
if (response.isSuccessful) {
withContext(Dispatchers.Main) {
castAndCrew.value = response.body()
}
}
}
}
enum class DetailViewType { enum class DetailViewType {
MOVIE, MOVIE,
TV TV

View File

@@ -14,6 +14,7 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.owenlejeune.tvtime.preferences.AppPreferences import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.PaletteView import com.owenlejeune.tvtime.ui.components.PaletteView
@@ -67,7 +68,7 @@ private fun PaletteDialog(showDialog: MutableState<Boolean>) {
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { showDialog.value = false } onClick = { showDialog.value = false }
) { ) {
Text(text = "Dismiss") Text(text = "Dismiss", color = Color.White)
} }
}, },
confirmButton = {}, confirmButton = {},

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<corners android:radius="5dp" /> <corners android:radius="20dp" />
<solid android:color="@android:color/darker_gray" /> <solid android:color="@android:color/darker_gray" />
</shape> </shape>