feat(settings): add cinematic background toggle to settings

This commit is contained in:
tapframe 2026-04-03 02:34:04 +05:30
parent fe04d6cf72
commit a972630bdc
3 changed files with 84 additions and 1 deletions

View file

@ -38,10 +38,17 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
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.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import com.nuvio.app.core.ui.NuvioBackButton
import com.nuvio.app.core.ui.TraktListPickerDialog
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
@ -435,11 +442,31 @@ fun MetaDetailsScreen(
val isTablet = maxWidth >= 720.dp
val contentHorizontalPadding = if (isTablet) 32.dp else 18.dp
val contentMaxWidth = detailTabletContentMaxWidth(maxWidth, isTablet)
val cinematicEnabled = metaScreenSettingsUiState.cinematicBackground
Box(modifier = Modifier.fillMaxSize()) {
if (cinematicEnabled) {
val backdropUrl = meta.background ?: meta.poster
if (backdropUrl != null) {
AsyncImage(
model = backdropUrl,
contentDescription = null,
modifier = Modifier
.fillMaxSize()
.blur(30.dp),
contentScale = ContentScale.Crop,
)
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background.copy(alpha = 0.92f)),
)
}
}
Column(
modifier = Modifier
.fillMaxSize()
.zIndex(1f)
.verticalScroll(scrollState),
) {
DetailHero(
@ -524,13 +551,36 @@ fun MetaDetailsScreen(
}
}
if (cinematicEnabled && heroHeightPx > 0) {
val blendColor = MaterialTheme.colorScheme.background
Box(
modifier = Modifier
.zIndex(0.5f)
.fillMaxWidth()
.height(132.dp)
.graphicsLayer {
translationY = heroHeightPx.toFloat() - scrollState.value
}
.background(
Brush.verticalGradient(
colors = listOf(
blendColor.copy(alpha = 0.98f),
blendColor.copy(alpha = 0.84f),
blendColor.copy(alpha = 0.52f),
Color.Transparent,
),
),
),
)
}
if (headerProgress <= 0.05f) {
NuvioBackButton(
onClick = onBack,
modifier = Modifier.padding(
start = 12.dp,
top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 8.dp,
),
).zIndex(2f),
containerColor = MaterialTheme.colorScheme.background.copy(alpha = 0.5f),
contentColor = MaterialTheme.colorScheme.onBackground,
)
@ -542,6 +592,7 @@ fun MetaDetailsScreen(
progress = headerProgress,
onBack = onBack,
onToggleSaved = toggleSaved,
modifier = Modifier.zIndex(2f),
)
selectedEpisodeForActions?.let { selectedEpisode ->

View file

@ -31,6 +31,7 @@ data class MetaScreenSectionItem(
data class MetaScreenSettingsUiState(
val items: List<MetaScreenSectionItem> = emptyList(),
val cinematicBackground: Boolean = false,
)
@Serializable
@ -43,6 +44,7 @@ private data class StoredMetaScreenSectionPreference(
@Serializable
private data class StoredMetaScreenSettingsPayload(
val items: List<StoredMetaScreenSectionPreference> = emptyList(),
val cinematicBackground: Boolean = false,
)
private data class MetaScreenSectionDefinition(
@ -115,6 +117,7 @@ object MetaScreenSettingsRepository {
private var hasLoaded = false
private var preferences: MutableMap<MetaScreenSectionKey, StoredMetaScreenSectionPreference> = mutableMapOf()
private var cinematicBackground: Boolean = false
fun ensureLoaded() {
if (hasLoaded) return
@ -126,6 +129,7 @@ object MetaScreenSettingsRepository {
json.decodeFromString<StoredMetaScreenSettingsPayload>(payload)
}.getOrNull()
if (parsed != null) {
cinematicBackground = parsed.cinematicBackground
preferences = parsed.items.mapNotNull { item ->
val key = runCatching { MetaScreenSectionKey.valueOf(item.key) }.getOrNull() ?: return@mapNotNull null
key to item
@ -141,13 +145,22 @@ object MetaScreenSettingsRepository {
fun onProfileChanged() {
hasLoaded = false
preferences.clear()
cinematicBackground = false
_uiState.value = MetaScreenSettingsUiState()
ensureLoaded()
}
fun setCinematicBackground(enabled: Boolean) {
ensureLoaded()
cinematicBackground = enabled
publish()
persist()
}
fun clearLocalState() {
hasLoaded = false
preferences.clear()
cinematicBackground = false
_uiState.value = MetaScreenSettingsUiState()
}
@ -160,6 +173,7 @@ object MetaScreenSettingsRepository {
fun resetToDefaults() {
ensureLoaded()
preferences.clear()
cinematicBackground = false
normalizePreferences()
publish()
persist()
@ -221,6 +235,7 @@ object MetaScreenSettingsRepository {
order = preference?.order ?: 0,
)
},
cinematicBackground = cinematicBackground,
)
}
@ -229,6 +244,7 @@ object MetaScreenSettingsRepository {
json.encodeToString(
StoredMetaScreenSettingsPayload(
items = preferences.values.sortedBy { it.order },
cinematicBackground = cinematicBackground,
),
),
)

View file

@ -42,6 +42,22 @@ internal fun LazyListScope.metaScreenSettingsContent(
isTablet: Boolean,
uiState: MetaScreenSettingsUiState,
) {
item {
SettingsSection(
title = "APPEARANCE",
isTablet = isTablet,
) {
SettingsGroup(isTablet = isTablet) {
SettingsSwitchRow(
title = "Cinematic Background",
description = "Blurred backdrop behind content, similar to stream screen.",
checked = uiState.cinematicBackground,
isTablet = isTablet,
onCheckedChange = { MetaScreenSettingsRepository.setCinematicBackground(it) },
)
}
}
}
item {
SettingsSection(
title = "SECTIONS",