From 7644b4c586ea0d0d9e1f1361bc95a30511bfaebc Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:00:06 -0500 Subject: [PATCH] Minor UI changes (#407) - Semi-bold the movies or series title on the details pages to match home page - Shows total subtitle track count if subtitles disabled - Remove the springy animations from the play/watch/favorite/more buttons - Go back a page when playback finishes and there's nothing left in the queue Closes #393 --- .../wholphin/ui/components/PlayButtons.kt | 66 ++++++++----------- .../ui/components/SeriesComponents.kt | 2 + .../ui/components/VideoStreamDetails.kt | 57 ++++++++++------ .../ui/detail/movie/MovieDetailsHeader.kt | 2 + .../wholphin/ui/playback/PlaybackViewModel.kt | 2 +- 5 files changed, 70 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/PlayButtons.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/PlayButtons.kt index 6a14fdbf..0f99f8bc 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/PlayButtons.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/PlayButtons.kt @@ -78,39 +78,37 @@ fun ExpandablePlayButtons( if (resumePosition > Duration.ZERO) { item("play") { ExpandablePlayButton( - R.string.resume, - resumePosition, - Icons.Default.PlayArrow, - playOnClick, - Modifier - .onFocusChanged(buttonOnFocusChanged) - .focusRequester(firstFocus) - .animateItem(), + title = R.string.resume, + resume = resumePosition, + icon = Icons.Default.PlayArrow, + onClick = playOnClick, + modifier = + Modifier + .onFocusChanged(buttonOnFocusChanged) + .focusRequester(firstFocus), ) } item("restart") { ExpandablePlayButton( - R.string.restart, - Duration.ZERO, - Icons.Default.Refresh, - playOnClick, - Modifier - .onFocusChanged(buttonOnFocusChanged) - .animateItem(), + title = R.string.restart, + resume = Duration.ZERO, + icon = Icons.Default.Refresh, + onClick = playOnClick, + modifier = Modifier.onFocusChanged(buttonOnFocusChanged), mirrorIcon = true, ) } } else { item("play") { ExpandablePlayButton( - R.string.play, - Duration.ZERO, - Icons.Default.PlayArrow, - playOnClick, - Modifier - .onFocusChanged(buttonOnFocusChanged) - .focusRequester(firstFocus) - .animateItem(), + title = R.string.play, + resume = Duration.ZERO, + icon = Icons.Default.PlayArrow, + onClick = playOnClick, + modifier = + Modifier + .onFocusChanged(buttonOnFocusChanged) + .focusRequester(firstFocus), ) } } @@ -121,10 +119,7 @@ fun ExpandablePlayButtons( title = if (watched) R.string.mark_unwatched else R.string.mark_watched, iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash, onClick = watchOnClick, - modifier = - Modifier - .onFocusChanged(buttonOnFocusChanged) - .animateItem(), + modifier = Modifier.onFocusChanged(buttonOnFocusChanged), ) } @@ -135,23 +130,18 @@ fun ExpandablePlayButtons( iconStringRes = R.string.fa_heart, onClick = favoriteOnClick, iconColor = if (favorite) Color.Red else Color.Unspecified, - modifier = - Modifier - .onFocusChanged(buttonOnFocusChanged) - .animateItem(), + modifier = Modifier.onFocusChanged(buttonOnFocusChanged), ) } // More button item("more") { ExpandablePlayButton( - R.string.more, - Duration.ZERO, - Icons.Default.MoreVert, - { moreOnClick.invoke() }, - Modifier - .onFocusChanged(buttonOnFocusChanged) - .animateItem(), + title = R.string.more, + resume = Duration.ZERO, + icon = Icons.Default.MoreVert, + onClick = { moreOnClick.invoke() }, + modifier = Modifier.onFocusChanged(buttonOnFocusChanged), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt index 36cbc904..bdb94ba5 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt @@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.components import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text @@ -23,6 +24,7 @@ fun SeriesName( text = seriesName ?: "", color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.SemiBold, maxLines = 2, overflow = TextOverflow.Ellipsis, modifier = modifier, 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 44e2705d..9d83bc59 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 @@ -9,7 +9,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -103,14 +106,16 @@ fun VideoStreamDetails( } val audioCount = remember(source) { source?.audioStreamCount ?: 0 } val audio = - if (audioCount == 0 || audioStream == null) { - stringResource(R.string.none) - } else { - listOfNotNull( - languageName(audioStream.language), - formatAudioCodec(context, audioStream.codec, audioStream.profile), - audioStream.channelLayout, - ).joinToString(" ") + remember(audioCount, audioStream) { + if (audioCount == 0 || audioStream == null) { + context.getString(R.string.none) + } else { + listOfNotNull( + languageName(audioStream.language), + formatAudioCodec(context, audioStream.codec, audioStream.profile), + audioStream.channelLayout, + ).joinToString(" ") + } } StreamLabel( text = audio, @@ -132,17 +137,22 @@ fun VideoStreamDetails( remember(source) { (source?.embeddedSubtitleCount ?: 0) + (source?.externalSubtitlesCount ?: 0) } + var disabled by remember { mutableStateOf(false) } val subtitle = - if (itemPlayback?.subtitleIndex == TrackIndex.DISABLED) { - stringResource(R.string.disabled) - } else if (subtitleCount == 0 || subtitleStream == null) { - null - } else { - listOfNotNull( - languageName(subtitleStream.language), - "SDH".takeIf { subtitleStream.isHearingImpaired }, - formatSubtitleCodec(subtitleStream.codec), - ).joinToString(" ") + remember(subtitleCount, subtitleStream, itemPlayback) { + if (itemPlayback?.subtitleIndex == TrackIndex.DISABLED) { + disabled = true + context.getString(R.string.disabled) + } else if (subtitleCount == 0 || subtitleStream == null) { + null + } else { + disabled = false + listOfNotNull( + languageName(subtitleStream.language), + "SDH".takeIf { subtitleStream.isHearingImpaired }, + formatSubtitleCodec(subtitleStream.codec), + ).joinToString(" ") + } } subtitle?.let { StreamLabel( @@ -150,6 +160,7 @@ fun VideoStreamDetails( count = subtitleCount, icon = R.string.fa_closed_captioning, modifier = Modifier.widthIn(max = 160.dp), + disabled = disabled, ) } } @@ -190,6 +201,7 @@ fun StreamLabel( modifier: Modifier = Modifier, @StringRes icon: Int? = null, count: Int = 0, + disabled: Boolean = false, ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -222,9 +234,10 @@ fun StreamLabel( overflow = TextOverflow.Ellipsis, modifier = Modifier, ) - if (count > 1) { + val countToUse = if (disabled) count else count - 1 + if (countToUse > 0) { Text( - text = "(+${count - 1})", + text = "(+$countToUse)", maxLines = 1, modifier = Modifier, ) @@ -248,6 +261,10 @@ private fun StreamLabelPreview() { StreamLabel("HDR") StreamLabel("H264") StreamLabel("AC3 5.1", icon = R.string.fa_volume_high, count = 2) + + StreamLabel("PGS", count = 1) + StreamLabel("PGS", count = 1, disabled = true) + StreamLabel("PGS", count = 2) } } } 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 71115a9a..fcd15f7e 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 @@ -13,6 +13,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -53,6 +54,7 @@ fun MovieDetailsHeader( text = movie.name ?: "", color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.displaySmall, + fontWeight = FontWeight.SemiBold, maxLines = 2, overflow = TextOverflow.Ellipsis, modifier = Modifier.fillMaxWidth(.75f), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt index 2f08ee96..5672d673 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt @@ -722,7 +722,7 @@ class PlaybackViewModel withContext(Dispatchers.Main) { nextUp.value = nextItem if (nextItem == null) { - controllerViewState.showControls() + navigationManager.goBack() } } }