diff --git a/app/src/main/java/com/github/damontecres/wholphin/MainContent.kt b/app/src/main/java/com/github/damontecres/wholphin/MainContent.kt index 4de2ab2f..714d80dc 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainContent.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -36,6 +37,8 @@ import com.github.damontecres.wholphin.ui.components.AppScreensaver import com.github.damontecres.wholphin.ui.nav.ApplicationContent import com.github.damontecres.wholphin.ui.setup.SwitchServerContent import com.github.damontecres.wholphin.ui.setup.SwitchUserContent +import com.github.damontecres.wholphin.ui.util.InterfaceCustomization +import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization @Composable fun MainContent( @@ -55,64 +58,22 @@ fun MainContent( ) { // val backStack = rememberNavBackStack(SetupDestination.Loading) // setupNavigationManager.backStack = backStack - NavDisplay( - backStack = backStack, - onBack = { backStack.removeLastOrNull() }, - entryDecorators = - listOf( - rememberSaveableStateHolderNavEntryDecorator(), - rememberViewModelStoreNavEntryDecorator(), - ), - entryProvider = { key -> - key as SetupDestination - NavEntry(key) { - when (key) { - SetupDestination.Loading -> { - Box( - modifier = Modifier.size(200.dp), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator( - color = MaterialTheme.colorScheme.border, - modifier = Modifier.align(Alignment.Center), - ) - } - } - - SetupDestination.ServerList -> { - SwitchServerContent(Modifier.fillMaxSize()) - } - - is SetupDestination.UserList -> { - SwitchUserContent( - server = key.server, - Modifier.fillMaxSize(), - ) - } - - is SetupDestination.AppContent -> { - LaunchedEffect(Unit) { - backdropService.clearBackdrop() - } - val current = key.current - var showContent by remember { - mutableStateOf(true) - } - LifecycleEventEffect(Lifecycle.Event.ON_STOP) { - if (!appPreferences.signInAutomatically) { - showContent = false - } - } - - if (showContent) { - ApplicationContent( - user = current.user, - server = current.server, - navigationManager = navigationManager, - preferences = preferences, - modifier = Modifier.fillMaxSize(), - ) - } else { + val interfaceCustomization = + remember(appPreferences) { InterfaceCustomization(appPreferences) } + CompositionLocalProvider(LocalInterfaceCustomization provides interfaceCustomization) { + NavDisplay( + backStack = backStack, + onBack = { backStack.removeLastOrNull() }, + entryDecorators = + listOf( + rememberSaveableStateHolderNavEntryDecorator(), + rememberViewModelStoreNavEntryDecorator(), + ), + entryProvider = { key -> + key as SetupDestination + NavEntry(key) { + when (key) { + SetupDestination.Loading -> { Box( modifier = Modifier.size(200.dp), contentAlignment = Alignment.Center, @@ -123,18 +84,64 @@ fun MainContent( ) } } + + SetupDestination.ServerList -> { + SwitchServerContent(Modifier.fillMaxSize()) + } + + is SetupDestination.UserList -> { + SwitchUserContent( + server = key.server, + Modifier.fillMaxSize(), + ) + } + + is SetupDestination.AppContent -> { + LaunchedEffect(Unit) { + backdropService.clearBackdrop() + } + val current = key.current + var showContent by remember { + mutableStateOf(true) + } + LifecycleEventEffect(Lifecycle.Event.ON_STOP) { + if (!appPreferences.signInAutomatically) { + showContent = false + } + } + + if (showContent) { + ApplicationContent( + user = current.user, + server = current.server, + navigationManager = navigationManager, + preferences = preferences, + modifier = Modifier.fillMaxSize(), + ) + } else { + Box( + modifier = Modifier.size(200.dp), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator( + color = MaterialTheme.colorScheme.border, + modifier = Modifier.align(Alignment.Center), + ) + } + } + } } } + }, + ) + val screenSaverState by screensaverService.state.collectAsState() + if (screenSaverState.enabled || screenSaverState.enabledTemp) { + AnimatedVisibility( + screenSaverState.show, + Modifier.fillMaxSize(), + ) { + AppScreensaver(appPreferences, Modifier.fillMaxSize()) } - }, - ) - val screenSaverState by screensaverService.state.collectAsState() - if (screenSaverState.enabled || screenSaverState.enabledTemp) { - AnimatedVisibility( - screenSaverState.show, - Modifier.fillMaxSize(), - ) { - AppScreensaver(appPreferences, Modifier.fillMaxSize()) } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt index efb21849..e6e70625 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt @@ -117,61 +117,71 @@ data class BaseItem( null }, quickDetails = - buildAnnotatedString { - val details = - buildList { - if (type == BaseItemKind.EPISODE) { - data.seasonEpisode?.let(::add) - data.premiereDate?.let { add(getDateFormatter().format(it)) } - } else if (type == BaseItemKind.SERIES) { - data.seriesProductionYears?.let(::add) - } else if (type == BaseItemKind.PHOTO) { - if (data.productionYear != null) { - add(data.productionYear!!.toString()) - } else if (data.premiereDate != null) { - add(data.premiereDate!!.toLocalDate().toString()) + QuickDetailsData( + basic = + buildAnnotatedString { + val details = + buildList { + if (type == BaseItemKind.EPISODE) { + data.seasonEpisode?.let(::add) + data.premiereDate?.let { add(getDateFormatter().format(it)) } + } else if (type == BaseItemKind.SERIES) { + data.seriesProductionYears?.let(::add) + } else if (type == BaseItemKind.PHOTO) { + if (data.productionYear != null) { + add(data.productionYear!!.toString()) + } else if (data.premiereDate != null) { + add(data.premiereDate!!.toLocalDate().toString()) + } + } else if (type == BaseItemKind.BOX_SET) { + data.productionYear?.let { add(it.toString()) } + data.childCount?.let { add("$it items") } + } else { + data.productionYear?.let { add(it.toString()) } + } + data.runTimeTicks + ?.ticks + ?.roundMinutes + ?.let { add(it.toString()) } + data.timeRemaining + ?.roundMinutes + ?.let { add("$it left") } + } + details.forEachIndexed { index, string -> + append(string) + if (index != details.lastIndex) { + dot() } - } else if (type == BaseItemKind.BOX_SET) { - data.productionYear?.let { add(it.toString()) } - data.childCount?.let { add("$it items") } - } else { - data.productionYear?.let { add(it.toString()) } } - data.runTimeTicks - ?.ticks - ?.roundMinutes - ?.let { add(it.toString()) } - data.timeRemaining - ?.roundMinutes - ?.let { add("$it left") } - } - details.forEachIndexed { index, string -> - append(string) - if (index != details.lastIndex) { - dot() - } - } - // TODO time remaining - - data.officialRating?.let { - dot() - append(it) - } - data.communityRating?.let { - dot() - append(String.format(Locale.getDefault(), "%.1f", it)) - appendInlineContent(id = "star") - } - data.criticRating?.let { - dot() - append("${it.toInt()}%") - if (it >= 60f) { - appendInlineContent(id = "fresh") - } else { - appendInlineContent(id = "rotten") - } - } - }, + }, + officialRating = + data.officialRating?.let { + buildAnnotatedString { + dot() + append(it) + } + }, + communityRating = + data.communityRating?.let { + buildAnnotatedString { + dot() + append(String.format(Locale.getDefault(), "%.1f", it)) + appendInlineContent(id = "star") + } + }, + criticRating = + data.criticRating?.let { + buildAnnotatedString { + dot() + append("${it.toInt()}%") + if (it >= 60f) { + appendInlineContent(id = "fresh") + } else { + appendInlineContent(id = "rotten") + } + } + }, + ), ) private fun dateAsIndex(): Int? = @@ -254,7 +264,14 @@ val BaseItemDto.aspectRatioFloat: Float? get() = width?.let { w -> height?.let { data class BaseItemUi( val episodeCornerText: String?, val episodeUnplayedCornerText: String?, - val quickDetails: AnnotatedString, + val quickDetails: QuickDetailsData, +) + +data class QuickDetailsData( + val basic: AnnotatedString, + val officialRating: AnnotatedString? = null, + val criticRating: AnnotatedString? = null, + val communityRating: AnnotatedString? = null, ) /** diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt index fffdb7ec..228b31c2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt @@ -516,6 +516,25 @@ sealed interface AppPreference { summaryOff = R.string.disabled, ) + val DisplayTogglesPref = + AppMultiChoicePreference( + title = R.string.display_toggles_title, + summary = R.string.display_toggles_summary, + defaultValue = DisplayToggle.entries.filterNot { it == DisplayToggle.UNRECOGNIZED }, + allValues = DisplayToggle.entries.filterNot { it == DisplayToggle.UNRECOGNIZED }, + displayValues = R.array.display_toggle_types, + displayValuesSubtitles = R.array.display_toggle_types_subtitles, + getter = { + it.interfacePreferences.displayTogglesList + }, + setter = { prefs, value -> + prefs.updateInterfacePreferences { + clearDisplayToggles() + addAllDisplayToggles(value) + } + }, + ) + val InstalledVersion = AppClickablePreference( title = R.string.installed_version, @@ -1058,8 +1077,8 @@ val basicPreferences = preferences = listOf( AppPreference.SignInAuto, - AppPreference.PlayThemeMusic, AppPreference.RememberSelectedTab, + AppPreference.PlayThemeMusic, AppPreference.SubtitleStyle, AppPreference.ThemeColors, AppPreference.ScreensaverSettings, @@ -1172,13 +1191,13 @@ val advancedPreferences = preferences = listOf( AppPreference.ShowClock, + AppPreference.BackdropStylePref, AppPreference.ShowLogos, AppPreference.ManageMedia, AppPreference.CombineContinueNext, + AppPreference.DisplayTogglesPref, // Temporarily disabled, see https://github.com/damontecres/Wholphin/pull/127#issuecomment-3478058418 // AppPreference.NavDrawerSwitchOnFocus, - AppPreference.ControllerTimeout, - AppPreference.BackdropStylePref, AppPreference.SlideshowDuration, AppPreference.SlideshowPlayVideos, ), @@ -1190,6 +1209,7 @@ val advancedPreferences = preferences = listOf( AppPreference.OneClickPause, + AppPreference.ControllerTimeout, AppPreference.CinemaMode, AppPreference.GlobalContentScale, AppPreference.SkipSegments, @@ -1337,6 +1357,7 @@ data class AppMultiChoicePreference( override val getter: (prefs: Pref) -> List, override val setter: (prefs: Pref, value: List) -> Pref, @param:StringRes val summary: Int? = null, + @param:ArrayRes val displayValuesSubtitles: Int? = null, ) : AppPreference> data class AppClickablePreference( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/QuickDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/QuickDetails.kt index d4931d77..080e6bb7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/QuickDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/QuickDetails.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.text.InlineTextContent import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Star import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -21,82 +22,110 @@ import androidx.tv.material3.Icon import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import com.github.damontecres.wholphin.R +import com.github.damontecres.wholphin.data.model.QuickDetailsData +import com.github.damontecres.wholphin.preferences.DisplayToggle import com.github.damontecres.wholphin.ui.dot import com.github.damontecres.wholphin.ui.getTimeFormatter import com.github.damontecres.wholphin.ui.util.LocalClock +import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization import kotlin.time.Duration @Composable fun QuickDetails( - details: AnnotatedString, + details: QuickDetailsData?, timeRemaining: Duration?, modifier: Modifier = Modifier, textStyle: TextStyle = MaterialTheme.typography.titleSmall, ) { - val inlineContentMap = - remember(textStyle) { - mapOf( - "star" to - InlineTextContent( - Placeholder( - textStyle.fontSize, - textStyle.fontSize, - PlaceholderVerticalAlign.TextCenter, - ), - ) { - Icon( - imageVector = Icons.Filled.Star, - tint = FilledStarColor, - contentDescription = null, - modifier = Modifier.fillMaxSize(), - ) - }, - "rotten" to - InlineTextContent( - Placeholder( - textStyle.fontSize, - textStyle.fontSize, - PlaceholderVerticalAlign.TextCenter, - ), - ) { - Icon( - painter = painterResource(R.drawable.ic_rotten_tomatoes_rotten), - contentDescription = null, - modifier = Modifier.fillMaxSize(), - tint = Color.Unspecified, - ) - }, - "fresh" to - InlineTextContent( - Placeholder( - textStyle.fontSize, - textStyle.fontSize, - PlaceholderVerticalAlign.TextCenter, - ), - ) { - Icon( - painter = painterResource(R.drawable.ic_rotten_tomatoes_fresh), - contentDescription = null, - modifier = Modifier.fillMaxSize(), - tint = Color.Unspecified, - ) - }, - ) - } - + val enabled = LocalInterfaceCustomization.current.enabledDisplayToggles + val inlineContentMap = rememberQuickDetailsContentMap(textStyle) Row(modifier = modifier) { - Text( - text = details, - color = MaterialTheme.colorScheme.onSurface, - style = textStyle, - inlineContent = inlineContentMap, - maxLines = 1, - modifier = Modifier, - ) + if (details != null) { + QuickDetailsText(details.basic, Modifier, textStyle, inlineContentMap) + if (DisplayToggle.OFFICIAL_RATING in enabled) { + QuickDetailsText(details.officialRating, Modifier, textStyle, inlineContentMap) + } + if (DisplayToggle.COMMUNITY_RATING in enabled) { + QuickDetailsText(details.communityRating, Modifier, textStyle, inlineContentMap) + } + if (DisplayToggle.CRITIC_RATING in enabled) { + QuickDetailsText(details.criticRating, Modifier, textStyle, inlineContentMap) + } + } timeRemaining?.let { TimeRemaining(it, textStyle = textStyle) } } } +@NonRestartableComposable +@Composable +fun QuickDetailsText( + str: AnnotatedString?, + modifier: Modifier = Modifier, + textStyle: TextStyle = MaterialTheme.typography.titleSmall, + inlineContentMap: Map = rememberQuickDetailsContentMap(textStyle), +) = str?.let { + Text( + text = str, + color = MaterialTheme.colorScheme.onSurface, + style = textStyle, + inlineContent = inlineContentMap, + maxLines = 1, + modifier = modifier, + ) +} + +@Composable +fun rememberQuickDetailsContentMap(textStyle: TextStyle = MaterialTheme.typography.titleSmall) = + remember(textStyle) { + mapOf( + "star" to + InlineTextContent( + Placeholder( + textStyle.fontSize, + textStyle.fontSize, + PlaceholderVerticalAlign.TextCenter, + ), + ) { + Icon( + imageVector = Icons.Filled.Star, + tint = FilledStarColor, + contentDescription = null, + modifier = Modifier.fillMaxSize(), + ) + }, + "rotten" to + InlineTextContent( + Placeholder( + textStyle.fontSize, + textStyle.fontSize, + PlaceholderVerticalAlign.TextCenter, + ), + ) { + Icon( + painter = painterResource(R.drawable.ic_rotten_tomatoes_rotten), + contentDescription = null, + modifier = Modifier.fillMaxSize(), + tint = Color.Unspecified, + ) + }, + "fresh" to + InlineTextContent( + Placeholder( + textStyle.fontSize, + textStyle.fontSize, + PlaceholderVerticalAlign.TextCenter, + ), + ) { + Icon( + painter = painterResource(R.drawable.ic_rotten_tomatoes_fresh), + contentDescription = null, + modifier = Modifier.fillMaxSize(), + tint = Color.Unspecified, + ) + }, + ) + } + @Composable fun TimeRemaining( remaining: Duration, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt index d8647132..60d3806b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt @@ -24,7 +24,7 @@ import com.github.damontecres.wholphin.data.model.DiscoverRating import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.components.GenreText import com.github.damontecres.wholphin.ui.components.OverviewText -import com.github.damontecres.wholphin.ui.components.QuickDetails +import com.github.damontecres.wholphin.ui.components.QuickDetailsText import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.listToDotString @@ -100,7 +100,7 @@ fun DiscoverMovieDetailsHeader( } } - QuickDetails(details, null) + QuickDetailsText(details) movie.genres?.mapNotNull { it.name }?.letNotEmpty { GenreText(it, Modifier.padding(bottom = padding)) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt index 42d4ce05..4b6a6200 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt @@ -56,7 +56,7 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.GenreText import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.OverviewText -import com.github.damontecres.wholphin.ui.components.QuickDetails +import com.github.damontecres.wholphin.ui.components.QuickDetailsText import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo import com.github.damontecres.wholphin.ui.letNotEmpty @@ -477,7 +477,7 @@ fun DiscoverSeriesDetailsHeader( } } - QuickDetails(details, null) + QuickDetailsText(details) series.genres?.mapNotNull { it.name }?.letNotEmpty { GenreText(it, Modifier.padding(bottom = padding)) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/TvGuideHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/TvGuideHeader.kt index 7813cfb3..d0673c28 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/TvGuideHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/TvGuideHeader.kt @@ -21,7 +21,7 @@ import com.github.damontecres.wholphin.ui.CrossFadeFactory import com.github.damontecres.wholphin.ui.components.EpisodeName import com.github.damontecres.wholphin.ui.components.HeaderUtils import com.github.damontecres.wholphin.ui.components.OverviewText -import com.github.damontecres.wholphin.ui.components.QuickDetails +import com.github.damontecres.wholphin.ui.components.QuickDetailsText import com.github.damontecres.wholphin.ui.components.StreamLabel import com.github.damontecres.wholphin.ui.components.TitleOrLogo import kotlin.time.Duration.Companion.milliseconds @@ -65,9 +65,8 @@ fun TvGuideHeader( modifier = Modifier.padding(start = HeaderUtils.startPadding), ) { program?.quickDetails?.let { - QuickDetails( + QuickDetailsText( it, - null, modifier = Modifier.padding(vertical = 4.dp), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt index 6c5b299a..321cc76c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt @@ -33,6 +33,7 @@ import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.api.seerr.infrastructure.ClientException import com.github.damontecres.wholphin.data.model.DiscoverItem import com.github.damontecres.wholphin.data.model.DiscoverRating +import com.github.damontecres.wholphin.data.model.QuickDetailsData import com.github.damontecres.wholphin.data.model.SeerrItemType import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.services.BackdropService @@ -275,11 +276,13 @@ fun SeerrDiscoverPage( ?.let(::add) }.let { val rating = focusedItem?.id?.let { ratingMap[it] } - listToDotString( - it, - rating?.audienceRating, - rating?.criticRating?.toFloat(), - ) + val str = + listToDotString( + it, + rating?.audienceRating, + rating?.criticRating?.toFloat(), + ) + QuickDetailsData(str) } } HomePageHeader( 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 f7ac3e7d..6877937d 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 @@ -40,7 +40,6 @@ import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize @@ -52,6 +51,7 @@ import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.HomeRowConfig import com.github.damontecres.wholphin.data.model.HomeRowViewOptions +import com.github.damontecres.wholphin.data.model.QuickDetailsData import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.cards.BannerCard @@ -546,7 +546,7 @@ fun HomePageHeader( subtitle = if (isEpisode) dto?.name else null, overview = dto?.overview, overviewTwoLines = isEpisode, - quickDetails = item?.ui?.quickDetails ?: AnnotatedString(""), + quickDetails = item?.ui?.quickDetails, timeRemaining = item?.timeRemainingOrRuntime, showLogo = showLogo, logoImageUrl = rememberLogoUrl(item), @@ -560,7 +560,7 @@ fun HomePageHeader( subtitle: String?, overview: String?, overviewTwoLines: Boolean, - quickDetails: AnnotatedString?, + quickDetails: QuickDetailsData?, timeRemaining: Duration?, showLogo: Boolean, logoImageUrl: String?, @@ -585,7 +585,7 @@ fun HomePageHeader( if (subtitle != null) { EpisodeName(subtitle) } - QuickDetails(quickDetails ?: AnnotatedString(""), timeRemaining) + QuickDetails(quickDetails, timeRemaining) val overviewModifier = Modifier .padding(0.dp) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/ComposablePreference.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/ComposablePreference.kt index 6d96667a..4a7338e2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/ComposablePreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/ComposablePreference.kt @@ -199,6 +199,7 @@ fun ComposablePreference( is AppMultiChoicePreference<*, *> -> { val values = stringArrayResource(preference.displayValues) + val subtitles = preference.displayValuesSubtitles?.let { stringArrayResource(it) } val summary = preference.summary?.let { stringResource(it) } ?: preference.summary(context, value) @@ -219,6 +220,9 @@ fun ComposablePreference( valueDisplay = { index, _ -> Text(values[index]) }, + subtitleDisplay = { index, _ -> + subtitles?.getOrNull(index)?.let { Text(it) } + }, ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/MultiChoicePreference.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/MultiChoicePreference.kt index bb34207f..4a9787a0 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/MultiChoicePreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/MultiChoicePreference.kt @@ -25,6 +25,7 @@ fun MultiChoicePreference( modifier: Modifier = Modifier, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, valueDisplay: @Composable (index: Int, item: T) -> Unit = { _, item -> Text(item.toString()) }, + subtitleDisplay: @Composable (index: Int, item: T) -> Unit = { _, item -> }, ) { // val values = stringArrayResource(preference.displayValues).toList() // val summary = @@ -60,6 +61,7 @@ fun MultiChoicePreference( possibleValues.mapIndexed { index, item -> DialogItem( headlineContent = { valueDisplay.invoke(index, item) }, + supportingContent = { subtitleDisplay.invoke(index, item) }, trailingContent = { Switch( checked = selectedValues.contains(item), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/ImageDetailsHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/ImageDetailsHeader.kt index 99675dc0..9ac3b6b1 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/ImageDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/ImageDetailsHeader.kt @@ -24,7 +24,6 @@ import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.QuickDetails import com.github.damontecres.wholphin.ui.components.StreamLabel import com.github.damontecres.wholphin.ui.components.VideoStreamDetails -import com.github.damontecres.wholphin.ui.isNotNullOrBlank import org.jellyfin.sdk.model.api.MediaType @OptIn(UnstableApi::class) @@ -64,11 +63,7 @@ fun ImageDetailsHeader( modifier = Modifier.fillMaxWidth(.75f), ) } - if (image.image.ui.quickDetails - .isNotNullOrBlank() - ) { - QuickDetails(image.image.ui.quickDetails, null) - } + QuickDetails(image.image.ui.quickDetails, null) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/util/InterfaceCustomization.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/util/InterfaceCustomization.kt new file mode 100644 index 00000000..30bcac68 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/util/InterfaceCustomization.kt @@ -0,0 +1,28 @@ +package com.github.damontecres.wholphin.ui.util + +import androidx.compose.runtime.staticCompositionLocalOf +import com.github.damontecres.wholphin.preferences.AppPreferences +import com.github.damontecres.wholphin.preferences.DisplayToggle +import java.util.EnumSet + +/** + * Represents various UI preferences made available via [LocalInterfaceCustomization] + */ +data class InterfaceCustomization( + val enabledDisplayToggles: EnumSet, +) { + constructor(prefs: AppPreferences) : this( + prefs.interfacePreferences.displayTogglesList.let { + if (it.isEmpty()) { + EnumSet.noneOf(DisplayToggle::class.java) + } else { + EnumSet.copyOf(it) + } + }, + ) +} + +val LocalInterfaceCustomization = + staticCompositionLocalOf { + InterfaceCustomization(EnumSet.allOf(DisplayToggle::class.java)) + } diff --git a/app/src/main/proto/WholphinDataStore.proto b/app/src/main/proto/WholphinDataStore.proto index c979402d..fb72875b 100644 --- a/app/src/main/proto/WholphinDataStore.proto +++ b/app/src/main/proto/WholphinDataStore.proto @@ -170,6 +170,12 @@ message SearchPreferences { bool combined_search_results = 1; } +enum DisplayToggle{ + OFFICIAL_RATING = 0; + CRITIC_RATING = 1; + COMMUNITY_RATING = 2; +} + message InterfacePreferences { ThemeSongVolume play_theme_songs = 1; bool remember_selected_tab = 2; @@ -185,6 +191,7 @@ message InterfacePreferences { bool enable_media_management = 12; bool show_logos = 13; SearchPreferences search_preferences = 14; + repeated DisplayToggle display_toggles = 15; } message AdvancedPreferences { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fe783615..3ea099a7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -726,6 +726,17 @@ @string/photos + + @string/official_rating + @string/critic_rating + @string/community_rating + + + + @string/tomatoes + @string/stars + + Search %s Actor @@ -779,6 +790,8 @@ For intros, credits, etc Play with Transcoding + Ratings display + Customize which to display Profile protection Continue watching/Next up click behavior Play on click @@ -793,4 +806,6 @@ + Stars + Tomatoes