diff --git a/app/src/main/java/com/owenlejeune/tvtime/extensions/NumberExtensions.kt b/app/src/main/java/com/owenlejeune/tvtime/extensions/NumberExtensions.kt index d16c743..977abf5 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/extensions/NumberExtensions.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/extensions/NumberExtensions.kt @@ -1,7 +1,14 @@ package com.owenlejeune.tvtime.extensions import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp fun Float.dpToPx(context: Context): Float { return this * context.resources.displayMetrics.density -} \ No newline at end of file +} + +@Composable +fun Int.toDp(): Dp = (this / LocalContext.current.resources.displayMetrics.density).toInt().dp diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Gallery.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Gallery.kt new file mode 100644 index 0000000..660c52c --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Gallery.kt @@ -0,0 +1,273 @@ +package com.owenlejeune.tvtime.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ChevronLeft +import androidx.compose.material.icons.outlined.ChevronRight +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.TileMode +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.HorizontalPager +import com.google.accompanist.pager.PagerState +import com.owenlejeune.tvtime.extensions.toDp +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlin.time.Duration.Companion.seconds + +@OptIn(ExperimentalPagerApi::class) +@Composable +fun Gallery( + pagerState: PagerState, + models: List, + modifier: Modifier = Modifier, + contentDescriptions: List = emptyList() +) { + HorizontalPager( + count = models.size, + state = pagerState, + modifier = modifier + ) { page -> + AsyncImage( + model = models[page], + contentDescription = contentDescriptions[page], + contentScale = ContentScale.FillWidth + ) + } +} + +@OptIn(ExperimentalPagerApi::class) +@Composable +fun TapGallery( + pagerState: PagerState, + models: List, + modifier: Modifier = Modifier +) { + val scope = rememberCoroutineScope() + + var showControls by remember { mutableStateOf(false) } + var lastTappedTime by remember { mutableStateOf(System.currentTimeMillis()) } + + var job: Job? = null + LaunchedEffect(lastTappedTime) { + if (showControls) { + job = scope.launch { + delay(5.seconds) + withContext(Dispatchers.Main) { + showControls = false + } + } + } + } + + Box( + modifier = modifier + .clickable { + job?.cancel() + showControls = true + lastTappedTime = System.currentTimeMillis() + } + ) { + val sizeImage = remember { mutableStateOf(IntSize.Zero) } + HorizontalPager( + count = models.size, + state = pagerState, + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .onGloballyPositioned { sizeImage.value = it.size } + ) { page -> + AsyncImage( + model = models[page], + contentDescription = null, + contentScale = ContentScale.FillWidth + ) + } + + AnimatedVisibility( + visible = showControls, + enter = fadeIn(), + exit = fadeOut() + ) { + val gradient = Brush.horizontalGradient( + colors = listOf(Color.Black, Color.Transparent), + startX = 0f, + endX = (sizeImage.value.width/2).toFloat(), + tileMode = TileMode.Mirror + ) + + Box( + modifier = Modifier + .background(brush = gradient) + .size( + width = sizeImage.value.width.toDp(), + height = sizeImage.value.height.toDp() + ) + ) { + IconButton( + modifier = Modifier + .fillMaxHeight() + .width(width = 48.dp) + .align(Alignment.CenterStart), + onClick = {} + ) { + Icon( + imageVector = Icons.Outlined.ChevronLeft, + contentDescription = null, + modifier = Modifier.align(Alignment.Center), + tint = MaterialTheme.colorScheme.surface + ) + } + + IconButton( + modifier = Modifier + .fillMaxHeight() + .width(width = 48.dp) + .align(Alignment.CenterEnd), + onClick = {} + ) { + Icon( + imageVector = Icons.Outlined.ChevronRight, + contentDescription = null, + modifier = Modifier.align(Alignment.Center), + tint = MaterialTheme.colorScheme.surface + ) + } +// Box( +// modifier = Modifier +// .background(brush = leftGradient) +// .height(height = sizeImage.value.height.toDp()) +// .width((sizeImage.value.width/2).toDp()) +// .align(Alignment.CenterStart) +// .onGloballyPositioned { leftSizeImage.value = it.size } +// .clickable { +// val target = +// if (pagerState.currentPage == 0) models.size - 1 else pagerState.currentPage - 1 +// scope.launch { pagerState.animateScrollToPage(target) } +// } +// ) { +// Icon( +// imageVector = Icons.Outlined.ChevronLeft, +// contentDescription = null, +// modifier = Modifier +// .size(48.dp) +// .padding(start = 24.dp) +// .align(Alignment.CenterStart), +// tint = MaterialTheme.colorScheme.surface +// ) +// } +// Box( +// modifier = Modifier +// .background(brush = rightGradient) +// .height(height = sizeImage.value.height.toDp()) +// .width((sizeImage.value.width/2).toDp()) +// .align(Alignment.CenterEnd) +// .onGloballyPositioned { rightSizeImage.value = it.size } +// .clickable { +// val target = +// if (pagerState.currentPage == models.size - 1) 0 else pagerState.currentPage + 1 +// scope.launch { pagerState.animateScrollToPage(target) } +// } +// ) { +// Icon( +// imageVector = Icons.Outlined.ChevronRight, +// contentDescription = null, +// modifier = Modifier +// .size(48.dp) +// .padding(end = 24.dp) +// .align(Alignment.CenterEnd), +// tint = MaterialTheme.colorScheme.surface +// ) +// } + } + } + +// AnimatedVisibility( +// visible = showControls.value, +// enter = fadeIn(), +// exit = fadeOut() +// ) { +// val leftSizeImage = remember { mutableStateOf(IntSize.Zero) } +// val leftGradient = Brush.horizontalGradient( +// colors = listOf(Color.Black, Color.Transparent), +// startX = 0f, +// endX = leftSizeImage.value.width.toFloat() +// ) +// Box( +// modifier = Modifier +// .background(brush = leftGradient) +// .fillMaxHeight() +// .width(100.dp) +// .align(Alignment.CenterStart) +// .onGloballyPositioned { leftSizeImage.value = it.size } +// .clickable { +// val target = +// if (pagerState.currentPage == 0) models.size - 1 else pagerState.currentPage + 1 +// scope.launch { pagerState.animateScrollToPage(target) } +// } +// ) { +// Icon( +// imageVector = Icons.Outlined.ChevronLeft, +// contentDescription = null, +// modifier = Modifier.size(48.dp) +// ) +// } +// +// val rightSizeImage = remember { mutableStateOf(IntSize.Zero) } +// val rightGradient = Brush.horizontalGradient( +// colors = listOf(Color.Black, Color.Transparent), +// startX = leftSizeImage.value.width.toFloat(), +// endX = 0f +// ) +// Box( +// modifier = Modifier +// .background(brush = rightGradient) +// .fillMaxHeight() +// .width(100.dp) +// .align(Alignment.CenterEnd) +// .onGloballyPositioned { rightSizeImage.value = it.size } +// .clickable { +// val target = +// if (pagerState.currentPage == models.size - 1) 0 else pagerState.currentPage - 1 +// scope.launch { pagerState.animateScrollToPage(target) } +// } +// ) { +// Icon( +// imageVector = Icons.Outlined.ChevronRight, +// contentDescription = null, +// modifier = Modifier.size(48.dp) +// ) +// } +// } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Overlays.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Overlays.kt new file mode 100644 index 0000000..d49d041 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Overlays.kt @@ -0,0 +1,41 @@ +package com.owenlejeune.tvtime.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.rememberPagerState +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection +import com.owenlejeune.tvtime.utils.TmdbUtils + +@OptIn(ExperimentalPagerApi::class) +@Composable +fun ImageGalleryOverlay( + imageCollection: ImageCollection, + selectedImage: Int, + onDismissRequest: () -> Unit +) { + Box( + modifier = Modifier + .fillMaxSize() + .background(color = Color.Black.copy(alpha = 0.7f)) + .clickable(onClick = onDismissRequest) + ) { + val pagerState = rememberPagerState(initialPage = selectedImage) + TapGallery( + pagerState = pagerState, + models = imageCollection.backdrops.map { TmdbUtils.getFullBackdropPath(it) }, + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .align(Alignment.Center) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/DetailViewCommon.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/DetailViewCommon.kt index cace922..8a12bf0 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/DetailViewCommon.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/DetailViewCommon.kt @@ -2,6 +2,7 @@ package com.owenlejeune.tvtime.ui.screens.main import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme @@ -20,6 +21,7 @@ import coil.compose.AsyncImage import coil.compose.rememberAsyncImagePainter import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager +import com.google.accompanist.pager.PagerState import com.google.accompanist.pager.rememberPagerState import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection @@ -31,15 +33,18 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +@OptIn(ExperimentalPagerApi::class) @Composable fun DetailHeader( modifier: Modifier = Modifier, + showGalleryOverlay: MutableState? = null, imageCollection: ImageCollection? = null, backdropUrl: String? = null, posterUrl: String? = null, backdropContentDescription: String? = null, posterContentDescription: String? = null, - rating: Float? = null + rating: Float? = null, + pagerState: PagerState? = null ) { ConstraintLayout(modifier = modifier .fillMaxWidth() @@ -56,8 +61,12 @@ fun DetailHeader( top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(parent.end) + } + .clickable { + showGalleryOverlay?.value = true }, - imageCollection = imageCollection + imageCollection = imageCollection, + state = pagerState ) } else { Backdrop( @@ -148,15 +157,17 @@ private fun Backdrop( @OptIn(ExperimentalPagerApi::class) @Composable -private fun BackdropGallery( +fun BackdropGallery( modifier: Modifier, - imageCollection: ImageCollection? + imageCollection: ImageCollection?, + delayMillis: Long = 5000, + state: PagerState? = null ) { BackdropContainer( modifier = modifier ) { sizeImage -> if (imageCollection != null) { - val pagerState = rememberPagerState() + val pagerState = state ?: rememberPagerState(initialPage = 0) HorizontalPager( count = imageCollection.backdrops.size, state = pagerState, @@ -173,14 +184,16 @@ private fun BackdropGallery( } // fixes an issue where using pagerState.current page breaks paging animations - var key by remember { mutableStateOf(false) } - LaunchedEffect(key1 = key) { - launch { - delay(5000) - with(pagerState) { - val target = if (currentPage < pageCount - 1) currentPage + 1 else 0 - animateScrollToPage(target) - key = !key + if (delayMillis > 0) { + var key by remember { mutableStateOf(false) } + LaunchedEffect(key1 = key) { + launch { + delay(delayMillis) + with(pagerState) { + val target = if (currentPage < pageCount - 1) currentPage + 1 else 0 + animateScrollToPage(target) + key = !key + } } } } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt index e2f3074..b00055b 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt @@ -1,6 +1,7 @@ package com.owenlejeune.tvtime.ui.screens.main import android.content.Context +import android.media.MediaActionSound import android.widget.Toast import androidx.compose.animation.* import androidx.compose.animation.core.tween @@ -31,6 +32,9 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.PagerState +import com.google.accompanist.pager.rememberPagerState import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.api.v3.AccountService import com.owenlejeune.tvtime.api.tmdb.api.v3.DetailService @@ -51,16 +55,17 @@ import com.owenlejeune.tvtime.utils.TmdbUtils import kotlinx.coroutines.* import org.json.JSONObject import org.koin.java.KoinJavaComponent +import org.koin.java.KoinJavaComponent.get import java.text.DecimalFormat -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class) @Composable fun MediaDetailView( appNavController: NavController, itemId: Int?, type: MediaViewType, windowSize: WindowSizeClass, - preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java) + preferences: AppPreferences = get(AppPreferences::class.java) ) { val service = when (type) { MediaViewType.MOVIE -> MoviesService() @@ -75,108 +80,153 @@ fun MediaDetailView( } } + val images = remember { mutableStateOf(null) } + itemId?.let { + if (preferences.showBackdropGallery && images.value == null) { + fetchImages(itemId, service, images) + } + } + val decayAnimationSpec = rememberSplineBasedDecay() val topAppBarScrollState = rememberTopAppBarScrollState() val scrollBehavior = remember(decayAnimationSpec) { TopAppBarDefaults.pinnedScrollBehavior(topAppBarScrollState) } - Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - topBar = { - SmallTopAppBar( - scrollBehavior = scrollBehavior, - colors = TopAppBarDefaults - .smallTopAppBarColors( - scrolledContainerColor = MaterialTheme.colorScheme.background, - titleContentColor = MaterialTheme.colorScheme.primary - ), - title = { Text(text = mediaItem.value?.title ?: "") }, - navigationIcon = { - IconButton( - onClick = { appNavController.popBackStack() } - ) { - Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.content_description_back_button), - tint = MaterialTheme.colorScheme.primary - ) + val pagerState = rememberPagerState(initialPage = 0) + + Box( + modifier = Modifier.fillMaxSize() + ) { + val showGalleryOverlay = remember { mutableStateOf(false) } + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + SmallTopAppBar( + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults + .smallTopAppBarColors( + scrolledContainerColor = MaterialTheme.colorScheme.background, + titleContentColor = MaterialTheme.colorScheme.primary + ), + title = { Text(text = mediaItem.value?.title ?: "") }, + navigationIcon = { + IconButton( + onClick = { appNavController.popBackStack() } + ) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = stringResource(id = R.string.content_description_back_button), + tint = MaterialTheme.colorScheme.primary + ) + } } - } - ) + ) + } + ) { innerPadding -> + Box(modifier = Modifier.padding(innerPadding)) { + MediaViewContent( + appNavController = appNavController, + itemId = itemId, + mediaItem = mediaItem, + images = images, + service = service, + type = type, + windowSize = windowSize, + showImageGallery = showGalleryOverlay, + pagerState = pagerState + ) + } } - ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - Row( - modifier = Modifier - .background(color = MaterialTheme.colorScheme.background), - horizontalArrangement = Arrangement.spacedBy(16.dp) + + if (showGalleryOverlay.value) { + images.value?.let { + ImageGalleryOverlay( + imageCollection = it, + selectedImage = pagerState.currentPage, + onDismissRequest = { showGalleryOverlay.value = false } + ) + } + } + } +} + +@OptIn(ExperimentalPagerApi::class) +@Composable +private fun MediaViewContent( + appNavController: NavController, + itemId: Int?, + mediaItem: MutableState, + images: MutableState, + service: DetailService, + type: MediaViewType, + windowSize: WindowSizeClass, + showImageGallery: MutableState, + pagerState: PagerState +) { + Row( + modifier = Modifier + .background(color = MaterialTheme.colorScheme.background), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Column( + modifier = Modifier + .background(color = MaterialTheme.colorScheme.background) + .weight(1f) + .verticalScroll(state = rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + DetailHeader( + posterUrl = TmdbUtils.getFullPosterPath(mediaItem.value?.posterPath), + posterContentDescription = mediaItem.value?.title, + backdropUrl = TmdbUtils.getFullBackdropPath(mediaItem.value?.backdropPath), + rating = mediaItem.value?.voteAverage?.let { it / 10 }, + imageCollection = images.value, + showGalleryOverlay = showImageGallery, + pagerState = pagerState + ) + + Column( + modifier = Modifier.padding(horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) ) { - Column( - modifier = Modifier - .background(color = MaterialTheme.colorScheme.background) - .weight(1f) - .verticalScroll(state = rememberScrollState()), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - val images = remember { mutableStateOf(null) } - itemId?.let { - if (preferences.showBackdropGallery && images.value == null) { - fetchImages(itemId, service, images) - } - } - - DetailHeader( - posterUrl = TmdbUtils.getFullPosterPath(mediaItem.value?.posterPath), - posterContentDescription = mediaItem.value?.title, - backdropUrl = TmdbUtils.getFullBackdropPath(mediaItem.value?.backdropPath), - rating = mediaItem.value?.voteAverage?.let { it / 10 }, - imageCollection = images.value - ) - - Column( - modifier = Modifier.padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - if (type == MediaViewType.MOVIE) { - MiscMovieDetails(mediaItem = mediaItem, service as MoviesService) - } else { - MiscTvDetails(mediaItem = mediaItem, service as TvService) - } - - ActionsView(itemId = itemId, type = type, service = service) - - OverviewCard(itemId = itemId, mediaItem = mediaItem, service = service) - - CastCard(itemId = itemId, service = service, appNavController = appNavController) - - SimilarContentCard(itemId = itemId, service = service, mediaType = type, appNavController = appNavController) - - VideosCard(itemId = itemId, service = service) - - AdditionalDetailsCard(itemId = itemId, mediaItem = mediaItem, service = service, type = type) - - if (windowSize != WindowSizeClass.Expanded) { - ReviewsCard(itemId = itemId, service = service) - } - } - - Spacer(modifier = Modifier.height(16.dp)) + if (type == MediaViewType.MOVIE) { + MiscMovieDetails(mediaItem = mediaItem, service as MoviesService) + } else { + MiscTvDetails(mediaItem = mediaItem, service as TvService) } - if (windowSize == WindowSizeClass.Expanded) { - Column( - modifier = Modifier - .background(color = MaterialTheme.colorScheme.background) - .weight(1f) - .verticalScroll(state = rememberScrollState()) - ) { - ReviewsCard(itemId = itemId, service = service) + ActionsView(itemId = itemId, type = type, service = service) - Spacer(modifier = Modifier.height(16.dp)) - } + OverviewCard(itemId = itemId, mediaItem = mediaItem, service = service) + + CastCard(itemId = itemId, service = service, appNavController = appNavController) + + SimilarContentCard(itemId = itemId, service = service, mediaType = type, appNavController = appNavController) + + VideosCard(itemId = itemId, service = service) + + AdditionalDetailsCard(itemId = itemId, mediaItem = mediaItem, service = service, type = type) + + if (windowSize != WindowSizeClass.Expanded) { + ReviewsCard(itemId = itemId, service = service) } } + + Spacer(modifier = Modifier.height(16.dp)) + } + + if (windowSize == WindowSizeClass.Expanded) { + Column( + modifier = Modifier + .background(color = MaterialTheme.colorScheme.background) + .weight(1f) + .verticalScroll(state = rememberScrollState()) + ) { + ReviewsCard(itemId = itemId, service = service) + + Spacer(modifier = Modifier.height(16.dp)) + } } } } @@ -616,55 +666,57 @@ private fun OverviewCard( } mediaItem.value?.let { mi -> - ContentCard( - modifier = modifier - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .padding(vertical = 12.dp, horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + if (!mi.tagline.isNullOrEmpty() || keywordResponse.value?.keywords?.isNotEmpty() == true || !mi.overview.isNullOrEmpty()) { + ContentCard( + modifier = modifier ) { - mi.tagline?.let { tagline -> - if (tagline.isNotEmpty()) { - Text( - text = tagline, - color = MaterialTheme.colorScheme.primary, - style = MaterialTheme.typography.bodyLarge, - fontStyle = FontStyle.Italic, - ) - } - } - Text( - text = mi.overview ?: "", - color = MaterialTheme.colorScheme.onSurfaceVariant, - style = MaterialTheme.typography.bodyMedium - ) - - - keywordResponse.value?.keywords?.let { keywords -> - val keywordsChipInfo = keywords.map { ChipInfo(it.name, false) } - Row( - modifier = Modifier.horizontalScroll(rememberScrollState()), - horizontalArrangement = Arrangement.spacedBy(ChipStyle.Rounded.mainAxisSpacing) - ) { - keywordsChipInfo.forEach { keywordChipInfo -> - RoundedChip( - text = keywordChipInfo.text, - enabled = keywordChipInfo.enabled, - colors = ChipDefaults.roundedChipColors( - unselectedContainerColor = MaterialTheme.colorScheme.primary, - unselectedContentColor = MaterialTheme.colorScheme.primary - ), - onSelectionChanged = { chip -> - if (service is MoviesService) { - // Toast.makeText(context, chip, Toast.LENGTH_SHORT).show() - } - } + Column( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(vertical = 12.dp, horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + mi.tagline?.let { tagline -> + if (tagline.isNotEmpty()) { + Text( + text = tagline, + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.bodyLarge, + fontStyle = FontStyle.Italic, ) } } + Text( + text = mi.overview ?: "", + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium + ) + + + keywordResponse.value?.keywords?.let { keywords -> + val keywordsChipInfo = keywords.map { ChipInfo(it.name, false) } + Row( + modifier = Modifier.horizontalScroll(rememberScrollState()), + horizontalArrangement = Arrangement.spacedBy(ChipStyle.Rounded.mainAxisSpacing) + ) { + keywordsChipInfo.forEach { keywordChipInfo -> + RoundedChip( + text = keywordChipInfo.text, + enabled = keywordChipInfo.enabled, + colors = ChipDefaults.roundedChipColors( + unselectedContainerColor = MaterialTheme.colorScheme.primary, + unselectedContentColor = MaterialTheme.colorScheme.primary + ), + onSelectionChanged = { chip -> + if (service is MoviesService) { + // Toast.makeText(context, chip, Toast.LENGTH_SHORT).show() + } + } + ) + } + } + } } } } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleDetailView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleDetailView.kt index 101f013..6c02fb6 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleDetailView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleDetailView.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.navigation.NavController +import com.google.accompanist.pager.ExperimentalPagerApi import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.api.v3.PeopleService import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailPerson @@ -33,7 +34,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class) @Composable fun PersonDetailView( appNavController: NavController,