From d3e74a17ff2281a4c192092c5eb454cb78fad583 Mon Sep 17 00:00:00 2001 From: Aditya Date: Sat, 2 May 2026 14:55:18 +0530 Subject: [PATCH 1/4] Change dialog scrim color for better text contrast Updated dialog scrim color to adapt to themes and improve text visibility. --- .../mpvex/ui/components/liquid/LiquidAlertDialog.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidAlertDialog.kt b/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidAlertDialog.kt index 4c776e250..acc8fac5c 100644 --- a/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidAlertDialog.kt +++ b/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidAlertDialog.kt @@ -20,6 +20,12 @@ fun LiquidAlertDialog( ) { val backdrop = LocalLiquidBackdrop.current + // CHANGED: dialog scrim was previously `Color.White.copy(alpha = 0.15f)`. That flat 15%-opaque white was the + // root cause of the dialog text bleeding into menu/background text — and it didn't adapt to dark theme. + // New scrim: theme color (`surfaceContainerHigh`) at 0.85 alpha. Adapts to light/dark automatically and is + // opaque enough that text behind the dialog can't compete with text inside it. + val scrimColor = MaterialTheme.colorScheme.surfaceContainerHigh.copy(alpha = 0.85f) + AlertDialog( onDismissRequest = onDismissRequest, confirmButton = confirmButton, @@ -27,10 +33,9 @@ fun LiquidAlertDialog( icon = icon, title = title, text = text, - // The Magic Sauce: If Liquid is enabled, it strips the grey background and makes it a transparent glass pane! modifier = if (backdrop != null) { modifier.background( - color = Color.White.copy(alpha = 0.15f), + color = scrimColor, shape = MaterialTheme.shapes.extraLarge ) } else modifier, From 8636769fdce60193abb677521fda0a7ddf0706a4 Mon Sep 17 00:00:00 2001 From: Aditya Date: Sat, 2 May 2026 14:57:07 +0530 Subject: [PATCH 2/4] Introduce shared LiquidUIPreferences CompositionLocal Added LocalLiquidPreferences to share LiquidUIPreferences across the component tree. --- .../mpvex/ui/components/liquid/LiquidComponents.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidComponents.kt b/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidComponents.kt index 2de4cbeaf..ac41d3156 100644 --- a/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidComponents.kt +++ b/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidComponents.kt @@ -17,10 +17,17 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.dp import com.kyant.backdrop.Backdrop import app.marlboroadvance.mpvex.preferences.LiquidTarget +import app.marlboroadvance.mpvex.preferences.LiquidUIPreferences // Broadcasts the glass camera to any button that wants it! val LocalLiquidBackdrop = androidx.compose.runtime.staticCompositionLocalOf { null } +// ADDED: shared LiquidUIPreferences CompositionLocal. +// Why: previously every LiquidGlassSurface built its own DataStore wrapper via remember{ LiquidUIPreferences(context) }. +// With many glass surfaces on screen (nav + buttons + cards) that meant N wrappers all observing the same DataStore. +// MainActivity can now provide one instance for the whole tree; null fallback keeps old behavior working. +val LocalLiquidPreferences = androidx.compose.runtime.staticCompositionLocalOf { null } + @OptIn(ExperimentalFoundationApi::class) @Composable fun TransparentLiquidButton( From 8778ab4dce730c2d5d043c4a9a589f7cfdb65a02 Mon Sep 17 00:00:00 2001 From: Aditya Date: Sat, 2 May 2026 14:59:13 +0530 Subject: [PATCH 3/4] Refactor LiquidGlassSurface for performance improvements Refactor LiquidGlassSurface to use shared LocalLiquidPreferences for better performance and prevent unnecessary recompositions. Adjust tint alpha handling and improve the draw logic for different API levels. --- .../components/liquid/LiquidGlassSurface.kt | 79 +++++++++++++------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidGlassSurface.kt b/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidGlassSurface.kt index 15a48c3cb..83f8aa170 100644 --- a/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidGlassSurface.kt +++ b/app/src/main/java/app/marlboroadvance/mpvex/ui/components/liquid/LiquidGlassSurface.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import com.kyant.backdrop.Backdrop @@ -28,49 +29,75 @@ import app.marlboroadvance.mpvex.preferences.LiquidTarget @Composable fun LiquidGlassSurface( backdrop: Backdrop, - target: LiquidTarget = LiquidTarget.NAV, // Targets the Nav bar by default + target: LiquidTarget = LiquidTarget.NAV, modifier: Modifier = Modifier, - shape: Shape = RoundedCornerShape(24.dp), - defaultTintColor: Color = Color.White, + shape: Shape = RoundedCornerShape(24.dp), + defaultTintColor: Color = Color.White, content: @Composable () -> Unit ) { val context = LocalContext.current - val liquidPrefs = remember { LiquidUIPreferences(context) } - - // SAFE STATE COLLECTIONS: Only updates when the target actually changes - val blurRadius by remember(target) { liquidPrefs.blurRadiusFlow(target) }.collectAsState(initial = 0f) - val refractionHeight by remember(target) { liquidPrefs.refractionHeightFlow(target) }.collectAsState(initial = 40f) - val refractionAmount by remember(target) { liquidPrefs.refractionAmountFlow(target) }.collectAsState(initial = 23f) - val chromaticAberration by remember(target) { liquidPrefs.chromaticAberrationFlow(target) }.collectAsState(initial = false) - val depthEffect by remember(target) { liquidPrefs.depthEffectFlow(target) }.collectAsState(initial = true) - val vibrancyEnabled by remember(target) { liquidPrefs.vibrancyEnabledFlow(target) }.collectAsState(initial = true) - val tintAlpha by remember(target) { liquidPrefs.tintAlphaFlow(target) }.collectAsState(initial = 0.15f) + // CHANGED: prefer shared LocalLiquidPreferences if MainActivity provided one (perf: one DataStore wrapper for whole tree). + // Fallback to a remembered instance keyed on applicationContext so it survives configuration changes + // and is not rebuilt on every recomposition. + val sharedPrefs = LocalLiquidPreferences.current + val liquidPrefs = sharedPrefs ?: remember(context.applicationContext) { + LiquidUIPreferences(context.applicationContext) + } + + val blurRadius by remember(liquidPrefs, target) { liquidPrefs.blurRadiusFlow(target) }.collectAsState(initial = 0f) + val refractionHeight by remember(liquidPrefs, target) { liquidPrefs.refractionHeightFlow(target) }.collectAsState(initial = 40f) + val refractionAmount by remember(liquidPrefs, target) { liquidPrefs.refractionAmountFlow(target) }.collectAsState(initial = 23f) + val chromaticAberration by remember(liquidPrefs, target) { liquidPrefs.chromaticAberrationFlow(target) }.collectAsState(initial = false) + val depthEffect by remember(liquidPrefs, target) { liquidPrefs.depthEffectFlow(target) }.collectAsState(initial = true) + val vibrancyEnabled by remember(liquidPrefs, target) { liquidPrefs.vibrancyEnabledFlow(target) }.collectAsState(initial = true) + // CHANGED: initial value 0.15f → 0.5f to match the new DataStore default; prevents a flash of see-through glass + // (where backdrop text would bleed through) on first frame before the flow emits. + val tintAlpha by remember(liquidPrefs, target) { liquidPrefs.tintAlphaFlow(target) }.collectAsState(initial = 0.5f) + + val density = LocalDensity.current + // ADDED (perf): hoist dp→px conversions out of the per-frame `effects` draw lambda. + // Previously `refractionHeight.dp.toPx()` etc. ran on every frame inside drawBackdrop's effects block. + // Now they only recompute when density or the underlying pref value actually changes. + val blurPx = remember(density, blurRadius) { with(density) { blurRadius.dp.toPx() } } + val refractionHeightPx = remember(density, refractionHeight) { with(density) { refractionHeight.dp.toPx() } } + val refractionAmountPx = remember(density, refractionAmount) { with(density) { refractionAmount.dp.toPx() } } + // ADDED: clamp tint alpha to [0,1] defensively; a stray out-of-range pref value would otherwise crash drawRect. + val safeTintAlpha = tintAlpha.coerceIn(0f, 1f) - if (Build.VERSION.SDK_INT >= 33) { + if (Build.VERSION.SDK_INT >= 33) { Box( modifier = modifier .drawBackdrop( backdrop = backdrop, shape = { shape }, effects = { + // CHANGED: enforce Backdrop docs' required effect order — color filter (vibrancy) → blur → lens. + // ADDED (perf): skip lens() entirely when refraction params are 0 — the lens shader is + // the most expensive effect in this pipeline, and running it with zero amount is wasted GPU work. if (vibrancyEnabled) vibrancy() - if (blurRadius > 0f) blur(blurRadius.dp.toPx()) - - lens( - refractionHeight = refractionHeight.dp.toPx(), - refractionAmount = refractionAmount.dp.toPx(), - depthEffect = depthEffect, - chromaticAberration = chromaticAberration - ) + if (blurPx > 0f) blur(blurPx) + if (refractionHeightPx > 0f && refractionAmountPx > 0f) { + lens( + refractionHeight = refractionHeightPx, + refractionAmount = refractionAmountPx, + depthEffect = depthEffect, + chromaticAberration = chromaticAberration + ) + } }, - onDrawSurface = { drawRect(defaultTintColor.copy(alpha = tintAlpha)) } + onDrawSurface = { drawRect(defaultTintColor.copy(alpha = safeTintAlpha)) } ) ) { content() } - } else { + } else { + // CHANGED: pre-API-33 fallback alpha is now coerced to ≥ 0.7f. + // Reason: this branch can't run blur/lens shaders (RuntimeShader is API 33+), so the tint is the ONLY + // thing separating foreground text from backdrop content. 0.5f looked transparent here even though it + // works fine on API 33+ where blur/lens further obscure the backdrop. + val fallbackAlpha = safeTintAlpha.coerceAtLeast(0.7f) Box( modifier = modifier - .background(defaultTintColor.copy(alpha = tintAlpha), shape) - .border(1.dp, Color.White.copy(alpha = 0.2f), shape) + .background(defaultTintColor.copy(alpha = fallbackAlpha), shape) + .border(1.dp, Color.White.copy(alpha = 0.2f), shape) .clip(shape) ) { content() } } From 674795465c8dcbc3272191e97920b8d25794417e Mon Sep 17 00:00:00 2001 From: Aditya Date: Sat, 2 May 2026 15:01:45 +0530 Subject: [PATCH 4/4] Change default tint alpha value to improve readability Updated default tint alpha value from 0.15f to 0.5f for better text readability. --- .../marlboroadvance/mpvex/preferences/LiquidUIPreferences.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/marlboroadvance/mpvex/preferences/LiquidUIPreferences.kt b/app/src/main/java/app/marlboroadvance/mpvex/preferences/LiquidUIPreferences.kt index cdf3ff71d..f075dc372 100644 --- a/app/src/main/java/app/marlboroadvance/mpvex/preferences/LiquidUIPreferences.kt +++ b/app/src/main/java/app/marlboroadvance/mpvex/preferences/LiquidUIPreferences.kt @@ -47,7 +47,9 @@ class LiquidUIPreferences(context: Context) { fun blurRadiusFlow(target: LiquidTarget): Flow = dataStore.data.map { it[floatPreferencesKey("${target.id}_blur")] ?: 0f } fun refractionHeightFlow(target: LiquidTarget): Flow = dataStore.data.map { it[floatPreferencesKey("${target.id}_height")] ?: 40f } fun refractionAmountFlow(target: LiquidTarget): Flow = dataStore.data.map { it[floatPreferencesKey("${target.id}_amount")] ?: 23f } - fun tintAlphaFlow(target: LiquidTarget): Flow = dataStore.data.map { it[floatPreferencesKey("${target.id}_alpha")] ?: 0.15f } + // CHANGED: default raised 0.15f → 0.5f. Old value let backdrop text bleed through navigation/dialog + // glass; 0.5f is the Backdrop docs' recommended balance of "glass look" vs. text readability. + fun tintAlphaFlow(target: LiquidTarget): Flow = dataStore.data.map { it[floatPreferencesKey("${target.id}_alpha")] ?: 0.5f } fun chromaticAberrationFlow(target: LiquidTarget): Flow = dataStore.data.map { it[booleanPreferencesKey("${target.id}_chromatic")] ?: false } fun depthEffectFlow(target: LiquidTarget): Flow = dataStore.data.map { it[booleanPreferencesKey("${target.id}_depth")] ?: true }