diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/DetailsBackdropImage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/DetailsBackdropImage.kt index bb7ef78f..c02f7f22 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/DetailsBackdropImage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/DetailsBackdropImage.kt @@ -1,5 +1,6 @@ package com.github.damontecres.wholphin.ui.components +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -14,7 +15,7 @@ import coil3.compose.AsyncImage import com.github.damontecres.wholphin.ui.isNotNullOrBlank @Composable -fun DetailsBackdropImage( +fun BoxScope.DetailsBackdropImage( backdropImageUrl: String?, modifier: Modifier = Modifier, ) { @@ -23,10 +24,11 @@ fun DetailsBackdropImage( AsyncImage( model = backdropImageUrl, contentDescription = null, - contentScale = ContentScale.Crop, + contentScale = ContentScale.Fit, alignment = Alignment.TopEnd, modifier = modifier + .align(Alignment.TopEnd) .fillMaxHeight(.85f) .alpha(.75f) .drawWithContent { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt index d7409040..eb206f03 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt @@ -23,40 +23,65 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp +import androidx.tv.material3.LocalTextStyle import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.ui.PreviewTvSpec +import com.github.damontecres.wholphin.ui.theme.WholphinTheme @Composable fun DotSeparatedRow( texts: List, + rating: Float? = null, modifier: Modifier = Modifier, textStyle: TextStyle = MaterialTheme.typography.titleSmall, ) { - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically, - ) { - texts.forEachIndexed { index, text -> - Text( - text = text, - style = textStyle, - color = MaterialTheme.colorScheme.onSurface, - ) - if (index != texts.lastIndex) { - Box( - modifier = - Modifier - .padding(horizontal = 8.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.onSurface.copy(alpha = 1f)) - .size(4.dp), + CompositionLocalProvider(LocalTextStyle provides textStyle) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + texts.forEachIndexed { index, text -> + Text( + text = text, + style = textStyle, + color = MaterialTheme.colorScheme.onSurface, + ) + if (rating != null || index != texts.lastIndex) { + Box( + modifier = + Modifier + .padding(horizontal = 8.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.onSurface.copy(alpha = 1f)) + .size(4.dp), + ) + } + } + rating?.let { + SimpleStarRating( + communityRating = it, + modifier = Modifier, ) } } } } + +@PreviewTvSpec +@Composable +private fun DotSeparatedRowPreview() { + WholphinTheme { + DotSeparatedRow( + texts = listOf("2025", "1h 48m", "PG-13", "1h 30m left"), + rating = 7.5f, + modifier = Modifier, + ) + } +} diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt index d719344a..a1e8598b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Star import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -36,7 +37,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.tv.material3.Icon import androidx.tv.material3.LocalContentColor import androidx.tv.material3.MaterialTheme @@ -77,17 +77,17 @@ val ratingBarHeight: Dp = 32.dp fun SimpleStarRating( communityRating: Float?, modifier: Modifier = Modifier, -) = SimpleStarRating( - text = communityRating?.let { String.format(Locale.getDefault(), "%.1f", it) }, - modifier = modifier, - textColor = MaterialTheme.colorScheme.onSurface, -) +) = CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) { + SimpleStarRating( + text = communityRating?.let { String.format(Locale.getDefault(), "%.1f", it) }, + modifier = modifier, + ) +} @Composable fun SimpleStarRating( text: String?, modifier: Modifier = Modifier, - textColor: Color = LocalContentColor.current, starColor: Color = FilledStarColor, ) { Row( @@ -98,8 +98,6 @@ fun SimpleStarRating( if (text.isNotNullOrBlank()) { Text( text = text, - fontSize = 14.sp, - color = textColor, modifier = Modifier, ) Icon( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/VideoStreamDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/VideoStreamDetails.kt index 7b05366e..a84d3458 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/VideoStreamDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/VideoStreamDetails.kt @@ -197,7 +197,7 @@ fun StreamLabel( modifier = modifier .background( - MaterialTheme.colorScheme.secondaryContainer, + MaterialTheme.colorScheme.secondaryContainer.copy(alpha = .5f), // MaterialTheme.colorScheme.surfaceVariant, shape = RoundedCornerShape(4.dp), ).padding(vertical = 4.dp, horizontal = 6.dp), @@ -245,8 +245,8 @@ private fun StreamLabelPreview() { .padding(8.dp), ) { StreamLabel("1080p") - StreamLabel("H264") StreamLabel("HDR") + StreamLabel("H264") StreamLabel("AC3 5.1", icon = R.string.fa_volume_high, count = 2) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt index fcec24a5..aa90c35d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed @@ -61,7 +62,6 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.ExpandablePlayButtons import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.Optional -import com.github.damontecres.wholphin.ui.components.VideoStreamDetails import com.github.damontecres.wholphin.ui.components.chooseStream import com.github.damontecres.wholphin.ui.components.chooseVersionParams import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel @@ -391,7 +391,6 @@ fun MovieDetailsContent( Modifier .fillMaxWidth() .bringIntoViewRequester(bringIntoViewRequester), -// .padding(bottom = 16.dp), ) { MovieDetailsHeader( preferences = preferences, @@ -399,7 +398,10 @@ fun MovieDetailsContent( chosenStreams = chosenStreams, bringIntoViewRequester = bringIntoViewRequester, overviewOnClick = overviewOnClick, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .padding(top = 32.dp, bottom = 16.dp), ) ExpandablePlayButtons( resumePosition = resumePosition, @@ -420,18 +422,13 @@ fun MovieDetailsContent( } } }, - modifier = Modifier.focusRequester(focusRequesters[HEADER_ROW]), + modifier = + Modifier + .padding(bottom = 16.dp) + .focusRequester(focusRequesters[HEADER_ROW]), ) } } - item { - VideoStreamDetails( - preferences = preferences, - dto = dto, - itemPlayback = chosenStreams?.itemPlayback, - modifier = Modifier, - ) - } if (people.isNotEmpty()) { item { PersonRow( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt index f326a133..1ec2c81e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt @@ -4,18 +4,17 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -27,7 +26,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.components.DotSeparatedRow import com.github.damontecres.wholphin.ui.components.OverviewText -import com.github.damontecres.wholphin.ui.components.SimpleStarRating +import com.github.damontecres.wholphin.ui.components.VideoStreamDetails import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.timeRemaining @@ -54,51 +53,49 @@ fun MovieDetailsHeader( Text( text = movie.name ?: "", color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.displayMedium, + style = MaterialTheme.typography.displaySmall, maxLines = 2, overflow = TextOverflow.Ellipsis, modifier = Modifier.fillMaxWidth(.75f), ) Column( - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(4.dp), modifier = Modifier.fillMaxWidth(.60f), ) { - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - val details = + val padding = 8.dp + val details = + remember(dto) { buildList { dto.productionYear?.let { add(it.toString()) } val duration = dto.runTimeTicks?.ticks duration ?.roundMinutes ?.toString() - ?.let { - add(it) - } - dto.officialRating?.let(::add) + ?.let(::add) dto.timeRemaining?.roundMinutes?.let { add("$it left") } + dto.officialRating?.let(::add) } - DotSeparatedRow( - texts = details, - textStyle = MaterialTheme.typography.titleMedium, - modifier = Modifier, - ) - } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - SimpleStarRating( - dto.communityRating, - Modifier.height(20.dp), - ) - } + } + DotSeparatedRow( + texts = details, + rating = dto.communityRating, + textStyle = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(bottom = padding), + ) + + VideoStreamDetails( + preferences = preferences, + dto = dto, + itemPlayback = chosenStreams?.itemPlayback, + modifier = Modifier.padding(bottom = padding), + ) dto.taglines?.firstOrNull()?.let { tagline -> Text( text = tagline, style = MaterialTheme.typography.bodyLarge, + fontStyle = FontStyle.Italic, + modifier = Modifier, ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt index cb312824..e21e5dc6 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt @@ -11,7 +11,6 @@ import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.components.ExpandablePlayButtons -import com.github.damontecres.wholphin.ui.components.VideoStreamDetails import org.jellyfin.sdk.model.extensions.ticks import kotlin.time.Duration @@ -45,11 +44,5 @@ fun FocusedEpisodeFooter( buttonOnFocusChanged = {}, modifier = Modifier, ) - VideoStreamDetails( - preferences = preferences, - dto = dto, - itemPlayback = chosenStreams?.itemPlayback, - modifier = Modifier, - ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt index fc56c21e..9eb9330a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt @@ -2,10 +2,8 @@ package com.github.damontecres.wholphin.ui.detail.series import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow @@ -17,7 +15,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.components.DotSeparatedRow import com.github.damontecres.wholphin.ui.components.OverviewText -import com.github.damontecres.wholphin.ui.components.SimpleStarRating +import com.github.damontecres.wholphin.ui.components.VideoStreamDetails import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.seasonEpisode @@ -45,10 +43,8 @@ fun FocusedEpisodeHeader( overflow = TextOverflow.Ellipsis, modifier = Modifier, ) - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - val details = + val details = + remember(dto) { buildList { dto?.seasonEpisode?.let(::add) dto?.premiereDate?.let { add(formatDateTime(it)) } @@ -56,24 +52,23 @@ fun FocusedEpisodeHeader( duration ?.roundMinutes ?.toString() - ?.let { - add(it) - } - dto?.officialRating?.let(::add) + ?.let(::add) dto?.timeRemaining?.roundMinutes?.let { add("$it left") } + dto?.officialRating?.let(::add) } - DotSeparatedRow( - texts = details, - textStyle = MaterialTheme.typography.titleSmall, - ) - } - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - SimpleStarRating( - dto?.communityRating, - Modifier.height(20.dp), + } + DotSeparatedRow( + texts = details, + rating = dto?.communityRating, + textStyle = MaterialTheme.typography.titleSmall, + ) + + if (dto != null) { + VideoStreamDetails( + preferences = preferences, + dto = dto, + itemPlayback = chosenStreams?.itemPlayback, + modifier = Modifier, ) } OverviewText( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt index ef7e11e8..d1a6143a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.relocation.BringIntoViewRequester @@ -30,6 +29,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -60,7 +60,6 @@ import com.github.damontecres.wholphin.ui.components.ExpandablePlayButton import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.Optional import com.github.damontecres.wholphin.ui.components.OverviewText -import com.github.damontecres.wholphin.ui.components.SimpleStarRating import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo @@ -584,23 +583,18 @@ fun SeriesDetailsHeader( ) DotSeparatedRow( texts = details, + rating = dto.communityRating, textStyle = MaterialTheme.typography.titleMedium, ) - dto.genres?.letNotEmpty { Text( text = it.joinToString(", "), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface, + overflow = TextOverflow.Ellipsis, modifier = Modifier, ) } - - SimpleStarRating( - dto.communityRating, - Modifier.height(20.dp), - ) - dto.overview?.let { overview -> OverviewText( overview = overview, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt index 6cbecaad..249b8f13 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt @@ -502,6 +502,7 @@ fun HomePageHeader( if (details.isNotEmpty()) { DotSeparatedRow( texts = details, + rating = item.data.communityRating, textStyle = MaterialTheme.typography.bodyLarge, modifier = Modifier, )