mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-19 22:07:14 +00:00
feat: add dominant colour bg
This commit is contained in:
parent
d6d541ac0b
commit
8a68d9e923
8 changed files with 231 additions and 31 deletions
|
|
@ -427,6 +427,7 @@ kotlin {
|
|||
implementation(libs.androidx.lifecycle.runtimeCompose)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlinx.atomicfu)
|
||||
implementation(libs.kmpalette.core)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.kermit)
|
||||
implementation(libs.supabase.postgrest)
|
||||
|
|
|
|||
|
|
@ -742,6 +742,14 @@
|
|||
<string name="settings_meta_cast_description">Principal cast list.</string>
|
||||
<string name="settings_meta_cinematic_background">Cinematic Background</string>
|
||||
<string name="settings_meta_cinematic_background_description">Blurred backdrop behind content, similar to stream screen.</string>
|
||||
<string name="settings_meta_background_mode">Background Mode</string>
|
||||
<string name="settings_meta_background_mode_description">Choose how artwork appears behind metadata pages.</string>
|
||||
<string name="settings_meta_background_mode_normal">Normal</string>
|
||||
<string name="settings_meta_background_mode_normal_description">Use the standard app background.</string>
|
||||
<string name="settings_meta_background_mode_cinematic">Cinematic</string>
|
||||
<string name="settings_meta_background_mode_cinematic_description">Show a soft blurred backdrop behind the page.</string>
|
||||
<string name="settings_meta_background_mode_dominant">Dominant Color</string>
|
||||
<string name="settings_meta_background_mode_dominant_description">Match the page background to the main color from the backdrop.</string>
|
||||
<string name="settings_meta_collection">Collection</string>
|
||||
<string name="settings_meta_collection_description">Related collection or franchise rail.</string>
|
||||
<string name="settings_meta_comments">Comments</string>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.animation.AnimatedVisibilityScope
|
|||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.animation.SharedTransitionScope
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
|
|
@ -52,6 +53,7 @@ import androidx.compose.ui.draw.blur
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
|
|
@ -111,6 +113,7 @@ import com.nuvio.app.features.watchprogress.buildPlaybackVideoId
|
|||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
||||
import com.nuvio.app.features.watching.application.WatchingActions
|
||||
import com.nuvio.app.features.watching.application.WatchingState
|
||||
import com.kmpalette.extensions.painter.rememberPainterDominantColorState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
|
|
@ -755,15 +758,46 @@ fun MetaDetailsScreen(
|
|||
)
|
||||
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
val isTablet = maxWidth >= 720.dp
|
||||
val contentHorizontalPadding = if (isTablet) 32.dp else 18.dp
|
||||
val contentMaxWidth = detailTabletContentMaxWidth(maxWidth, isTablet)
|
||||
val cinematicEnabled = metaScreenSettingsUiState.cinematicBackground && deferredMetaWorkAllowed
|
||||
val backdropUrl = meta.background ?: meta.poster
|
||||
val backgroundMode = metaScreenSettingsUiState.backgroundMode
|
||||
val dominantColorEnabled = backgroundMode == MetaScreenBackgroundMode.DominantColor &&
|
||||
deferredMetaWorkAllowed &&
|
||||
!backdropUrl.isNullOrBlank()
|
||||
var dominantBackdropPainter by remember(meta.id, backdropUrl) {
|
||||
mutableStateOf<Painter?>(null)
|
||||
}
|
||||
val dominantColorState = rememberPainterDominantColorState(
|
||||
defaultColor = colorScheme.background,
|
||||
defaultOnColor = colorScheme.onBackground,
|
||||
)
|
||||
LaunchedEffect(dominantColorEnabled, dominantBackdropPainter) {
|
||||
val painter = dominantBackdropPainter
|
||||
if (dominantColorEnabled && painter != null) {
|
||||
runCatching { dominantColorState.updateFrom(painter) }
|
||||
}
|
||||
}
|
||||
val dominantBackdropTargetColor = if (dominantColorEnabled) {
|
||||
dominantBackdropBlendColor(dominantColorState.color, colorScheme.background)
|
||||
} else {
|
||||
colorScheme.background
|
||||
}
|
||||
val dominantBackdropColor by animateColorAsState(
|
||||
targetValue = dominantBackdropTargetColor,
|
||||
animationSpec = tween(
|
||||
durationMillis = 320,
|
||||
easing = LinearOutSlowInEasing,
|
||||
),
|
||||
label = "detail_dominant_backdrop_color",
|
||||
)
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
if (cinematicEnabled) {
|
||||
val backdropUrl = meta.background ?: meta.poster
|
||||
if (backdropUrl != null) {
|
||||
when (backgroundMode) {
|
||||
MetaScreenBackgroundMode.Normal -> Unit
|
||||
MetaScreenBackgroundMode.Cinematic -> if (deferredMetaWorkAllowed && backdropUrl != null) {
|
||||
AsyncImage(
|
||||
model = backdropUrl,
|
||||
contentDescription = null,
|
||||
|
|
@ -775,7 +809,14 @@ fun MetaDetailsScreen(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background.copy(alpha = 0.92f)),
|
||||
.background(colorScheme.background.copy(alpha = 0.92f)),
|
||||
)
|
||||
}
|
||||
MetaScreenBackgroundMode.DominantColor -> if (deferredMetaWorkAllowed) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(dominantBackdropColor),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -797,6 +838,12 @@ fun MetaDetailsScreen(
|
|||
heroTrailerReady = heroTrailerReady,
|
||||
heroTrailerPlayWhenReady = heroTrailerPlayWhenReady,
|
||||
heroTrailerMuted = heroTrailerMuted,
|
||||
heroGradientColor = dominantBackdropColor.takeIf { dominantColorEnabled },
|
||||
onBackdropLoaded = { painter ->
|
||||
if (dominantColorEnabled) {
|
||||
dominantBackdropPainter = painter
|
||||
}
|
||||
},
|
||||
onHeroTrailerMuteToggle = {
|
||||
HeroTrailerAudioState.toggleMuted()
|
||||
},
|
||||
|
|
@ -897,8 +944,9 @@ fun MetaDetailsScreen(
|
|||
}
|
||||
}
|
||||
|
||||
if (cinematicEnabled && heroHeightPx > 0) {
|
||||
val blendColor = MaterialTheme.colorScheme.background
|
||||
if (backgroundMode.usesBackdropBackground && deferredMetaWorkAllowed && heroHeightPx > 0) {
|
||||
val blendColor = dominantBackdropColor.takeIf { dominantColorEnabled }
|
||||
?: colorScheme.background
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.zIndex(0.5f)
|
||||
|
|
@ -1794,3 +1842,16 @@ private fun detailTabletContentMaxWidth(maxWidth: Dp, isTablet: Boolean): Dp =
|
|||
} else {
|
||||
(maxWidth * 0.6f).coerceIn(520.dp, 680.dp)
|
||||
}
|
||||
|
||||
private fun dominantBackdropBlendColor(dominantColor: Color, backgroundColor: Color): Color =
|
||||
backgroundColor.blendTowards(dominantColor, fraction = 0.42f)
|
||||
|
||||
private fun Color.blendTowards(target: Color, fraction: Float): Color {
|
||||
val clamped = fraction.coerceIn(0f, 1f)
|
||||
return Color(
|
||||
red = red + (target.red - red) * clamped,
|
||||
green = green + (target.green - green) * clamped,
|
||||
blue = blue + (target.blue - blue) * clamped,
|
||||
alpha = alpha + (target.alpha - alpha) * clamped,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ data class MetaScreenSectionItem(
|
|||
|
||||
data class MetaScreenSettingsUiState(
|
||||
val items: List<MetaScreenSectionItem> = emptyList(),
|
||||
val backgroundMode: MetaScreenBackgroundMode = MetaScreenBackgroundMode.Normal,
|
||||
val cinematicBackground: Boolean = false,
|
||||
val heroTrailerPlayback: Boolean = false,
|
||||
val tabLayout: Boolean = false,
|
||||
|
|
@ -49,6 +50,34 @@ data class MetaScreenSettingsUiState(
|
|||
val blurUnwatchedEpisodes: Boolean = false,
|
||||
)
|
||||
|
||||
enum class MetaScreenBackgroundMode {
|
||||
Normal,
|
||||
Cinematic,
|
||||
DominantColor,
|
||||
;
|
||||
|
||||
val usesBackdropBackground: Boolean
|
||||
get() = this != Normal
|
||||
|
||||
companion object {
|
||||
fun parse(raw: String?): MetaScreenBackgroundMode? = when (raw?.lowercase()) {
|
||||
"normal" -> Normal
|
||||
"cinematic" -> Cinematic
|
||||
"dominant_color" -> DominantColor
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun persist(mode: MetaScreenBackgroundMode): String = when (mode) {
|
||||
Normal -> "normal"
|
||||
Cinematic -> "cinematic"
|
||||
DominantColor -> "dominant_color"
|
||||
}
|
||||
|
||||
fun fromLegacyCinematic(enabled: Boolean): MetaScreenBackgroundMode =
|
||||
if (enabled) Cinematic else Normal
|
||||
}
|
||||
}
|
||||
|
||||
enum class MetaEpisodeCardStyle {
|
||||
Horizontal,
|
||||
List,
|
||||
|
|
@ -79,6 +108,8 @@ private data class StoredMetaScreenSectionPreference(
|
|||
@Serializable
|
||||
private data class StoredMetaScreenSettingsPayload(
|
||||
val items: List<StoredMetaScreenSectionPreference> = emptyList(),
|
||||
@SerialName("background_mode")
|
||||
val backgroundMode: String? = null,
|
||||
val cinematicBackground: Boolean = false,
|
||||
@SerialName("hero_trailer_playback")
|
||||
val heroTrailerPlayback: Boolean = false,
|
||||
|
|
@ -159,7 +190,7 @@ object MetaScreenSettingsRepository {
|
|||
|
||||
private var hasLoaded = false
|
||||
private var preferences: MutableMap<MetaScreenSectionKey, StoredMetaScreenSectionPreference> = mutableMapOf()
|
||||
private var cinematicBackground: Boolean = false
|
||||
private var backgroundMode: MetaScreenBackgroundMode = MetaScreenBackgroundMode.Normal
|
||||
private var heroTrailerPlayback: Boolean = false
|
||||
private var tabLayout: Boolean = false
|
||||
private var episodeCardStyle: MetaEpisodeCardStyle = MetaEpisodeCardStyle.Horizontal
|
||||
|
|
@ -176,7 +207,8 @@ object MetaScreenSettingsRepository {
|
|||
json.decodeFromString<StoredMetaScreenSettingsPayload>(payload)
|
||||
}.getOrNull()
|
||||
if (parsed != null) {
|
||||
cinematicBackground = parsed.cinematicBackground
|
||||
backgroundMode = MetaScreenBackgroundMode.parse(parsed.backgroundMode)
|
||||
?: MetaScreenBackgroundMode.fromLegacyCinematic(parsed.cinematicBackground)
|
||||
heroTrailerPlayback = parsed.heroTrailerPlayback
|
||||
tabLayout = parsed.tabLayout
|
||||
episodeCardStyle = MetaEpisodeCardStyle.parse(parsed.episodeCardStyle)
|
||||
|
|
@ -197,7 +229,7 @@ object MetaScreenSettingsRepository {
|
|||
fun onProfileChanged() {
|
||||
hasLoaded = false
|
||||
preferences.clear()
|
||||
cinematicBackground = false
|
||||
backgroundMode = MetaScreenBackgroundMode.Normal
|
||||
heroTrailerPlayback = false
|
||||
tabLayout = false
|
||||
episodeCardStyle = MetaEpisodeCardStyle.Horizontal
|
||||
|
|
@ -207,8 +239,12 @@ object MetaScreenSettingsRepository {
|
|||
}
|
||||
|
||||
fun setCinematicBackground(enabled: Boolean) {
|
||||
setBackgroundMode(MetaScreenBackgroundMode.fromLegacyCinematic(enabled))
|
||||
}
|
||||
|
||||
fun setBackgroundMode(mode: MetaScreenBackgroundMode) {
|
||||
ensureLoaded()
|
||||
cinematicBackground = enabled
|
||||
backgroundMode = mode
|
||||
publish()
|
||||
persist()
|
||||
}
|
||||
|
|
@ -257,7 +293,7 @@ object MetaScreenSettingsRepository {
|
|||
fun clearLocalState() {
|
||||
hasLoaded = false
|
||||
preferences.clear()
|
||||
cinematicBackground = false
|
||||
backgroundMode = MetaScreenBackgroundMode.Normal
|
||||
heroTrailerPlayback = false
|
||||
tabLayout = false
|
||||
episodeCardStyle = MetaEpisodeCardStyle.Horizontal
|
||||
|
|
@ -272,9 +308,10 @@ object MetaScreenSettingsRepository {
|
|||
tabLayout: Boolean,
|
||||
episodeCardStyle: MetaEpisodeCardStyle = MetaEpisodeCardStyle.Horizontal,
|
||||
blurUnwatchedEpisodes: Boolean = false,
|
||||
backgroundMode: MetaScreenBackgroundMode? = null,
|
||||
) {
|
||||
ensureLoaded()
|
||||
this.cinematicBackground = cinematicBackground
|
||||
this.backgroundMode = backgroundMode ?: MetaScreenBackgroundMode.fromLegacyCinematic(cinematicBackground)
|
||||
this.heroTrailerPlayback = heroTrailerPlayback
|
||||
this.tabLayout = tabLayout
|
||||
this.episodeCardStyle = episodeCardStyle
|
||||
|
|
@ -301,7 +338,7 @@ object MetaScreenSettingsRepository {
|
|||
fun resetToDefaults() {
|
||||
ensureLoaded()
|
||||
preferences.clear()
|
||||
cinematicBackground = false
|
||||
backgroundMode = MetaScreenBackgroundMode.Normal
|
||||
heroTrailerPlayback = false
|
||||
tabLayout = false
|
||||
episodeCardStyle = MetaEpisodeCardStyle.Horizontal
|
||||
|
|
@ -369,7 +406,8 @@ object MetaScreenSettingsRepository {
|
|||
tabGroup = preference?.tabGroup,
|
||||
)
|
||||
},
|
||||
cinematicBackground = cinematicBackground,
|
||||
backgroundMode = backgroundMode,
|
||||
cinematicBackground = backgroundMode.usesBackdropBackground,
|
||||
heroTrailerPlayback = heroTrailerPlayback,
|
||||
tabLayout = tabLayout,
|
||||
episodeCardStyle = episodeCardStyle,
|
||||
|
|
@ -382,7 +420,8 @@ object MetaScreenSettingsRepository {
|
|||
json.encodeToString(
|
||||
StoredMetaScreenSettingsPayload(
|
||||
items = preferences.values.sortedBy { it.order },
|
||||
cinematicBackground = cinematicBackground,
|
||||
backgroundMode = MetaScreenBackgroundMode.persist(backgroundMode),
|
||||
cinematicBackground = backgroundMode.usesBackdropBackground,
|
||||
heroTrailerPlayback = heroTrailerPlayback,
|
||||
tabLayout = tabLayout,
|
||||
episodeCardStyle = MetaEpisodeCardStyle.persist(episodeCardStyle),
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import androidx.compose.ui.graphics.Brush
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -62,6 +63,8 @@ fun DetailHero(
|
|||
heroTrailerReady: Boolean = false,
|
||||
heroTrailerPlayWhenReady: Boolean = false,
|
||||
heroTrailerMuted: Boolean = true,
|
||||
heroGradientColor: Color? = null,
|
||||
onBackdropLoaded: (Painter) -> Unit = {},
|
||||
onHeroTrailerMuteToggle: () -> Unit = {},
|
||||
onHeroTrailerReady: () -> Unit = {},
|
||||
onHeroTrailerEnded: () -> Unit = {},
|
||||
|
|
@ -81,6 +84,7 @@ fun DetailHero(
|
|||
val heroChromeTopPadding = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() +
|
||||
8.dp +
|
||||
((40.dp - muteIconSize) / 2)
|
||||
val bottomGradientColor = heroGradientColor ?: MaterialTheme.colorScheme.background
|
||||
var logoLoadError by remember(meta.id, meta.logo) {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
|
@ -111,9 +115,10 @@ fun DetailHero(
|
|||
translationY = scrollOffset * 0.5f
|
||||
scaleX = 1.08f
|
||||
scaleY = 1.08f
|
||||
},
|
||||
},
|
||||
alignment = if (isTablet) Alignment.TopCenter else Alignment.Center,
|
||||
contentScale = ContentScale.Crop,
|
||||
onSuccess = { state -> onBackdropLoaded(state.painter) },
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
|
|
@ -190,12 +195,12 @@ fun DetailHero(
|
|||
Brush.verticalGradient(
|
||||
colorStops = arrayOf(
|
||||
0.00f to Color.Transparent,
|
||||
0.16f to MaterialTheme.colorScheme.background.copy(alpha = 0.04f),
|
||||
0.32f to MaterialTheme.colorScheme.background.copy(alpha = 0.14f),
|
||||
0.50f to MaterialTheme.colorScheme.background.copy(alpha = 0.34f),
|
||||
0.68f to MaterialTheme.colorScheme.background.copy(alpha = 0.62f),
|
||||
0.84f to MaterialTheme.colorScheme.background.copy(alpha = 0.84f),
|
||||
1.00f to MaterialTheme.colorScheme.background,
|
||||
0.16f to bottomGradientColor.copy(alpha = 0.04f),
|
||||
0.32f to bottomGradientColor.copy(alpha = 0.14f),
|
||||
0.50f to bottomGradientColor.copy(alpha = 0.34f),
|
||||
0.68f to bottomGradientColor.copy(alpha = 0.62f),
|
||||
0.84f to bottomGradientColor.copy(alpha = 0.84f),
|
||||
1.00f to bottomGradientColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import com.nuvio.app.core.build.AppFeaturePolicy
|
|||
import com.nuvio.app.core.build.TrailerPlaybackMode
|
||||
import com.nuvio.app.core.ui.NuvioActionLabel
|
||||
import com.nuvio.app.features.details.MetaEpisodeCardStyle
|
||||
import com.nuvio.app.features.details.MetaScreenBackgroundMode
|
||||
import com.nuvio.app.features.details.MetaScreenSectionItem
|
||||
import com.nuvio.app.features.details.MetaScreenSectionKey
|
||||
import com.nuvio.app.features.details.MetaScreenSettingsRepository
|
||||
|
|
@ -64,8 +65,6 @@ import nuvio.composeapp.generated.resources.settings_meta_actions
|
|||
import nuvio.composeapp.generated.resources.settings_meta_actions_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_cast
|
||||
import nuvio.composeapp.generated.resources.settings_meta_cast_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_cinematic_background
|
||||
import nuvio.composeapp.generated.resources.settings_meta_cinematic_background_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_collection
|
||||
import nuvio.composeapp.generated.resources.settings_meta_collection_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_comments
|
||||
|
|
@ -84,6 +83,14 @@ import nuvio.composeapp.generated.resources.settings_meta_episodes
|
|||
import nuvio.composeapp.generated.resources.settings_meta_episodes_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_blur_unwatched_episodes
|
||||
import nuvio.composeapp.generated.resources.settings_meta_blur_unwatched_episodes_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode_cinematic
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode_cinematic_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode_dominant
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode_dominant_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode_normal
|
||||
import nuvio.composeapp.generated.resources.settings_meta_background_mode_normal_description
|
||||
import nuvio.composeapp.generated.resources.settings_meta_group_label
|
||||
import nuvio.composeapp.generated.resources.settings_meta_more_like_this
|
||||
import nuvio.composeapp.generated.resources.settings_meta_more_like_this_description
|
||||
|
|
@ -117,12 +124,10 @@ internal fun LazyListScope.metaScreenSettingsContent(
|
|||
isTablet = isTablet,
|
||||
) {
|
||||
SettingsGroup(isTablet = isTablet) {
|
||||
SettingsSwitchRow(
|
||||
title = stringResource(Res.string.settings_meta_cinematic_background),
|
||||
description = stringResource(Res.string.settings_meta_cinematic_background_description),
|
||||
checked = uiState.cinematicBackground,
|
||||
MetaBackgroundModeSelector(
|
||||
isTablet = isTablet,
|
||||
onCheckedChange = { MetaScreenSettingsRepository.setCinematicBackground(it) },
|
||||
selectedMode = uiState.backgroundMode,
|
||||
onModeSelected = MetaScreenSettingsRepository::setBackgroundMode,
|
||||
)
|
||||
if (showHeroTrailerPlaybackSetting) {
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
|
|
@ -181,6 +186,71 @@ internal fun LazyListScope.metaScreenSettingsContent(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun MetaBackgroundModeSelector(
|
||||
isTablet: Boolean,
|
||||
selectedMode: MetaScreenBackgroundMode,
|
||||
onModeSelected: (MetaScreenBackgroundMode) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = if (isTablet) 20.dp else 16.dp,
|
||||
vertical = if (isTablet) 18.dp else 14.dp,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_meta_background_mode),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.Medium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_meta_background_mode_description),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
MetaScreenBackgroundMode.entries.forEach { mode ->
|
||||
FilterChip(
|
||||
selected = selectedMode == mode,
|
||||
onClick = { onModeSelected(mode) },
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(mode.labelRes),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
border = FilterChipDefaults.filterChipBorder(
|
||||
enabled = true,
|
||||
selected = selectedMode == mode,
|
||||
borderColor = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f),
|
||||
selectedBorderColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.7f),
|
||||
),
|
||||
colors = FilterChipDefaults.filterChipColors(
|
||||
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.55f),
|
||||
selectedLabelColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = stringResource(selectedMode.descriptionRes),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetaSectionReorderableList(
|
||||
items: List<MetaScreenSectionItem>,
|
||||
|
|
@ -394,6 +464,20 @@ private fun TabGroupChip(
|
|||
)
|
||||
}
|
||||
|
||||
private val MetaScreenBackgroundMode.labelRes: StringResource
|
||||
get() = when (this) {
|
||||
MetaScreenBackgroundMode.Normal -> Res.string.settings_meta_background_mode_normal
|
||||
MetaScreenBackgroundMode.Cinematic -> Res.string.settings_meta_background_mode_cinematic
|
||||
MetaScreenBackgroundMode.DominantColor -> Res.string.settings_meta_background_mode_dominant
|
||||
}
|
||||
|
||||
private val MetaScreenBackgroundMode.descriptionRes: StringResource
|
||||
get() = when (this) {
|
||||
MetaScreenBackgroundMode.Normal -> Res.string.settings_meta_background_mode_normal_description
|
||||
MetaScreenBackgroundMode.Cinematic -> Res.string.settings_meta_background_mode_cinematic_description
|
||||
MetaScreenBackgroundMode.DominantColor -> Res.string.settings_meta_background_mode_dominant_description
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetaEpisodeCardStyleSelector(
|
||||
isTablet: Boolean,
|
||||
|
|
|
|||
|
|
@ -719,7 +719,7 @@ internal fun settingsSearchEntries(
|
|||
|
||||
val detailAppearanceSection = stringResource(Res.string.settings_meta_section_appearance)
|
||||
listOf(
|
||||
PlaybackSearchRow("meta-cinematic", stringResource(Res.string.settings_meta_cinematic_background), stringResource(Res.string.settings_meta_cinematic_background_description)),
|
||||
PlaybackSearchRow("meta-background-mode", stringResource(Res.string.settings_meta_background_mode), stringResource(Res.string.settings_meta_background_mode_description)),
|
||||
PlaybackSearchRow("meta-tabs", stringResource(Res.string.settings_meta_tab_layout), stringResource(Res.string.settings_meta_tab_layout_description)),
|
||||
PlaybackSearchRow("meta-episode-cards", stringResource(Res.string.settings_meta_episode_cards), stringResource(Res.string.settings_meta_episode_cards_description)),
|
||||
PlaybackSearchRow("meta-blur-episodes", stringResource(Res.string.settings_meta_blur_unwatched_episodes), stringResource(Res.string.settings_meta_blur_unwatched_episodes_description)),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ junit = "4.13.2"
|
|||
kotlin = "2.3.0"
|
||||
kotlinx-serialization = "1.8.1"
|
||||
atomicfu = "0.32.1"
|
||||
kmpalette = "4.0.0-beta02"
|
||||
ktor = "3.4.1"
|
||||
material3 = "1.11.0-alpha07"
|
||||
androidx-media3 = "1.8.0"
|
||||
|
|
@ -57,6 +58,7 @@ coil-network-ktor3 = { module = "io.coil-kt.coil3:coil-network-ktor3", version.r
|
|||
coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||
kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version.ref = "atomicfu" }
|
||||
kmpalette-core = { module = "com.materialkolor.palette:core", version.ref = "kmpalette" }
|
||||
ktor-client-android = { module = "io.ktor:ktor-client-android", version.ref = "ktor" }
|
||||
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
|
||||
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
|
||||
|
|
|
|||
Loading…
Reference in a new issue