From 3b322012cfee46ed40f28205b9e283502a81748b Mon Sep 17 00:00:00 2001 From: BoatingCloud <1759888028@qq.com> Date: Thu, 27 Aug 2026 22:45:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E6=95=9B=E7=8E=BB=E7=92=83=E9=99=8D?= =?UTF-8?q?=E7=BA=A7=E5=B9=B6=E5=AE=8C=E5=96=84=E6=B7=B1=E8=89=B2=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/myapplication/MainApp.kt | 13 +++++ .../myapplication/screens/LibraryScreen.kt | 4 +- .../myapplication/screens/SettingsScreen.kt | 4 +- .../example/myapplication/ui/LiquidGlass.kt | 55 +++++++------------ .../myapplication/ui/design/LumaComponents.kt | 18 ++++-- .../ui/design/LumaDesignSystem.kt | 16 ++++-- memory/2026-08-27.md | 6 ++ memory/MEMORY.md | 1 + memory/projects.md | 1 + 9 files changed, 69 insertions(+), 49 deletions(-) diff --git a/app/src/main/kotlin/com/example/myapplication/MainApp.kt b/app/src/main/kotlin/com/example/myapplication/MainApp.kt index 161b283..9b0214d 100644 --- a/app/src/main/kotlin/com/example/myapplication/MainApp.kt +++ b/app/src/main/kotlin/com/example/myapplication/MainApp.kt @@ -1,5 +1,6 @@ package com.example.myapplication +import android.app.Activity import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.fadeIn @@ -15,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf @@ -26,10 +28,12 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import androidx.core.view.WindowCompat import com.example.myapplication.R as Res import com.example.myapplication.data.EmbyRepository import com.example.myapplication.data.EmbyServer @@ -91,6 +95,15 @@ fun MainApp() { if (!showStartScreen) { MiuixTheme { + val view = LocalView.current + val isDark = AppTheme.isDark + SideEffect { + (view.context as? Activity)?.window?.let { window -> + val controller = WindowCompat.getInsetsController(window, view) + controller.isAppearanceLightStatusBars = !isDark + controller.isAppearanceLightNavigationBars = !isDark + } + } val backStackEntry by navController.currentBackStackEntryAsState() val currentRoute = backStackEntry?.destination?.route var lastRootTab by rememberSaveable { mutableIntStateOf(0) } diff --git a/app/src/main/kotlin/com/example/myapplication/screens/LibraryScreen.kt b/app/src/main/kotlin/com/example/myapplication/screens/LibraryScreen.kt index d2cd865..3d1e233 100644 --- a/app/src/main/kotlin/com/example/myapplication/screens/LibraryScreen.kt +++ b/app/src/main/kotlin/com/example/myapplication/screens/LibraryScreen.kt @@ -166,7 +166,7 @@ private fun ServerCard(server: EmbyServer, tint: Color, onClick: () -> Unit, onC var clicked by remember { mutableStateOf(false) } val pressBg by animateColorAsState(targetValue = if (clicked) LumaColors.glassHighlight.copy(alpha = 0.24f) else Color.Transparent, animationSpec = tween(durationMillis = if (clicked) 80 else 200), label = "pb") LaunchedEffect(clicked) { if (clicked) { delay(150); clicked = false } } - GlassSurface(modifier = Modifier.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { clicked = true; onClick() }, fill = tint.copy(alpha = 0.50f), cornerRadius = 20.dp, padding = PaddingValues(0.dp)) { + GlassSurface(modifier = Modifier.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { clicked = true; onClick() }, cornerRadius = 20.dp, padding = PaddingValues(0.dp)) { Row(modifier = Modifier.fillMaxWidth().background(pressBg).padding(14.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) { if (server.iconUrl.isNotBlank()) { @@ -222,7 +222,7 @@ private fun ServerGridCard(server: EmbyServer, tint: Color, onClick: () -> Unit, var clicked by remember { mutableStateOf(false) } val pressBg by animateColorAsState(targetValue = if (clicked) LumaColors.glassHighlight.copy(alpha = 0.24f) else Color.Transparent, animationSpec = tween(durationMillis = if (clicked) 80 else 200), label = "pb") LaunchedEffect(clicked) { if (clicked) { delay(150); clicked = false } } - GlassSurface(modifier = Modifier.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { clicked = true; onClick() }, fill = tint.copy(alpha = 0.50f), cornerRadius = 20.dp, padding = PaddingValues(0.dp)) { + GlassSurface(modifier = Modifier.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { clicked = true; onClick() }, cornerRadius = 20.dp, padding = PaddingValues(0.dp)) { Column(modifier = Modifier.fillMaxWidth().background(pressBg).padding(14.dp), horizontalAlignment = Alignment.Start) { Row(verticalAlignment = Alignment.CenterVertically) { if (server.iconUrl.isNotBlank()) { diff --git a/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt b/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt index 8cf6294..823ffed 100644 --- a/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt +++ b/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt @@ -75,6 +75,7 @@ import com.example.myapplication.ui.design.LumaShapes import com.example.myapplication.ui.design.LumaSpacing import com.example.myapplication.ui.design.LumaTypography import com.example.myapplication.ui.design.lumaGlass +import com.example.myapplication.ui.design.lumaFallbackGlass import com.kyant.backdrop.Backdrop import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.drawBackdrop @@ -467,7 +468,7 @@ private fun LanguageMenuOverlay( onDrawSurface = { drawRect(surface) }, ) } else { - Modifier.background(LumaColors.fallbackGlass) + Modifier.lumaFallbackGlass(shape, border = false) }, ) .border(0.7.dp, border, shape) @@ -505,6 +506,7 @@ private fun LanguageOption( Row( Modifier .fillMaxWidth() + .heightIn(min = 48.dp) .clip(RoundedCornerShape(18.dp)) .clickable(onClick = onClick) .padding(horizontal = 10.dp, vertical = 8.dp), diff --git a/app/src/main/kotlin/com/example/myapplication/ui/LiquidGlass.kt b/app/src/main/kotlin/com/example/myapplication/ui/LiquidGlass.kt index 12dc0ef..c30a607 100644 --- a/app/src/main/kotlin/com/example/myapplication/ui/LiquidGlass.kt +++ b/app/src/main/kotlin/com/example/myapplication/ui/LiquidGlass.kt @@ -98,6 +98,7 @@ import com.example.myapplication.ui.design.LumaShapes import com.example.myapplication.ui.design.LumaTypography import com.example.myapplication.ui.design.LumaGlassRuntime import com.example.myapplication.ui.design.lumaGlass +import com.example.myapplication.ui.design.lumaFallbackGlass internal data class NavTab( val icon: LumaIcon, @@ -209,9 +210,10 @@ fun SegmentedControl( modifier: Modifier = Modifier, ) { val backdrop = LocalGlassBackdrop.current + val containerShape = RoundedCornerShape(20.dp) Row( modifier - .clip(RoundedCornerShape(20.dp)) + .clip(containerShape) .then( if (backdrop != null && LumaGlassRuntime.useNativeBackdrop) { Modifier.drawBackdrop( @@ -224,10 +226,10 @@ fun SegmentedControl( onDrawSurface = { drawRect(GlassTokens.glassFillSoft) }, ) } else { - Modifier.background(GlassTokens.glassFillSoft) + Modifier.lumaFallbackGlass(containerShape, border = false) } ) - .border(0.5.dp, GlassTokens.glassBorder, RoundedCornerShape(20.dp)) + .border(0.5.dp, GlassTokens.glassBorder, containerShape) .padding(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp), ) { @@ -276,7 +278,7 @@ fun SegmentedControl( ) .then( if (sel && (backdrop == null || !LumaGlassRuntime.useNativeBackdrop)) { - Modifier.background(selectedFill) + Modifier.lumaFallbackGlass(RoundedCornerShape(16.dp), selected = true, border = false) } else { Modifier } @@ -472,7 +474,7 @@ fun LiquidGlassPopupOverlay( onDrawSurface = { drawRect(surface) }, ) } else { - Modifier.background(LumaColors.fallbackGlass) + Modifier.lumaFallbackGlass(shape, border = false) } ) .border(0.7.dp, border, shape) @@ -537,23 +539,13 @@ fun LiquidGlassButton( scaleY = scale alpha = if (enabled) 1f else 0.52f } - .clip(shape) - .then( - if (backdrop != null && LumaGlassRuntime.useNativeBackdrop) { - Modifier.drawBackdrop( - backdrop = backdrop, - shape = { RoundedRectangle(cornerRadius) }, - effects = { - vibrancy() - blur(8.dp.toPx()) - }, - onDrawSurface = { drawRect(fill) }, - ) - } else { - Modifier.background(fill) - } + .lumaGlass( + backdrop = backdrop, + radius = cornerRadius, + level = LumaGlassLevel.Interactive, + tint = fill, + borderColor = GlassTokens.glassBorder, ) - .border(0.5.dp, GlassTokens.glassBorder, shape) .clickable(enabled = enabled, indication = null, interactionSource = interactionSource, onClick = onClick) .padding(horizontal = 16.dp, vertical = 11.dp), verticalAlignment = Alignment.CenterVertically, @@ -716,20 +708,15 @@ fun GlassInputField( Text(label, fontSize = 13.sp, color = GlassTokens.subtext, fontWeight = FontWeight.Medium) val shape = RoundedCornerShape(12.dp) Box( - Modifier.fillMaxWidth().clip(shape) - .then( - if (backdrop != null && LumaGlassRuntime.useNativeBackdrop) { - Modifier.drawBackdrop( - backdrop = backdrop, - shape = { RoundedRectangle(12.dp) }, - effects = { vibrancy(); blur(8.dp.toPx()) }, - onDrawSurface = { drawRect(GlassTokens.glassFillSoft) }, - ) - } else { - Modifier.background(GlassTokens.glassFillSoft) - } + Modifier.fillMaxWidth() + .lumaGlass( + backdrop = backdrop, + radius = 12.dp, + level = LumaGlassLevel.Clear, + tint = GlassTokens.glassFillSoft, + border = false, ) - .border(1.dp, if (isError) GlassTokens.danger else GlassTokens.glassBorder, shape) + .border(0.7.dp, if (isError) GlassTokens.danger else GlassTokens.glassBorder, shape) .padding(horizontal = 14.dp, vertical = 12.dp), ) { BasicTextField( diff --git a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt index 9d3f877..9b4eb43 100644 --- a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt +++ b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt @@ -106,12 +106,18 @@ fun LumaSectionTitle( ) { Text(title, color = LumaColors.text, fontSize = LumaTypography.title, fontWeight = androidx.compose.ui.text.font.FontWeight.Bold) if (action != null) { - Text( - action, - color = LumaColors.accent, - fontSize = LumaTypography.secondary, - modifier = Modifier.clickable(enabled = onAction != null, indication = null, interactionSource = remember { MutableInteractionSource() }) { onAction?.invoke() }, - ) + Box( + Modifier + .heightIn(min = 48.dp) + .clickable( + enabled = onAction != null, + indication = null, + interactionSource = remember { MutableInteractionSource() }, + ) { onAction?.invoke() }, + contentAlignment = Alignment.Center, + ) { + Text(action, color = LumaColors.accent, fontSize = LumaTypography.secondary) + } } } } diff --git a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaDesignSystem.kt b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaDesignSystem.kt index 8ce3bb8..e2274e4 100644 --- a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaDesignSystem.kt +++ b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaDesignSystem.kt @@ -201,15 +201,13 @@ object LumaColors { val glassShadow: Color get() = if (isDark) Color.Black.copy(alpha = 0.32f) else Color(0xFF344054).copy(alpha = 0.10f) - /** Stable tint used when native backdrop sampling is disabled on an emulator or old API. */ - val fallbackGlass: Color - get() = if (isDark) Color(0xFF1A2330).copy(alpha = 0.86f) else Color(0xFFE4E9F0).copy(alpha = 0.86f) } /** Stable frosted fallback for devices where Kyant Backdrop is disabled. */ fun Modifier.lumaFallbackGlass( shape: Shape, selected: Boolean = false, + border: Boolean = true, ): Modifier { val light = !LumaColors.isDark val top = if (selected) { @@ -283,7 +281,7 @@ fun Modifier.lumaFallbackGlass( Modifier }, ) - .border(0.6.dp, edge, shape) + .then(if (border) Modifier.border(0.6.dp, edge, shape) else Modifier) .clip(shape) } @@ -331,7 +329,6 @@ fun Modifier.lumaGlass( val surfaceTint = tint ?: spec.tint val usesDefaultGlassTint = tint == null || tint == LumaColors.clearTint || tint == LumaColors.regularTint || tint == LumaColors.strongTint || tint == LumaColors.floatingTint - val fallbackSurface = if (usesDefaultGlassTint) LumaColors.fallbackGlass else surfaceTint val edgeColor = borderColor ?: LumaColors.glassBorder val safeBackdrop = backdrop.takeIf { LumaGlassRuntime.useNativeBackdrop } return this @@ -355,7 +352,14 @@ fun Modifier.lumaGlass( onDrawSurface = { drawRect(surfaceTint) }, ) } else { - Modifier.background(fallbackSurface) + if (usesDefaultGlassTint) { + Modifier.lumaFallbackGlass(shape, border = false) + } else { + Modifier.background( + surfaceTint.copy(alpha = surfaceTint.alpha.coerceAtMost(0.34f)), + shape, + ) + } }, ) .then(if (border) Modifier.border(0.6.dp, edgeColor, shape) else Modifier) diff --git a/memory/2026-08-27.md b/memory/2026-08-27.md index 4e167e4..88a4975 100644 --- a/memory/2026-08-27.md +++ b/memory/2026-08-27.md @@ -118,3 +118,9 @@ 文件变更: ui/AppBottomBar.kt、ui/design/LumaDesignSystem.kt、ui/liquidglass/LiquidBottomTabs.kt、navigation/AppNavHost.kt、Aurora 图标资源、memory/*。 教训: 模拟器仍不能安全启用 Kyant RenderEffect 采样,降级路径必须在最终尺寸上裁剪,原生路径只能在通过设备与帧率门控后启用。 标签: #lumaplayer #bottom-bar #aurora #liquid-glass #renderthread #build + +[PROJECT:lumaplayer] 公共玻璃降级与深色系统栏收尾 +结论: 分段控件、按钮、输入框、资源库菜单、主题菜单和语言菜单统一复用渐变毛玻璃降级,深色主题系统栏图标同步;Debug/Lint/Unit 构建及 API35 模拟器回归通过。 +文件变更: MainApp.kt、screens/LibraryScreen.kt、screens/SettingsScreen.kt、ui/LiquidGlass.kt、ui/design/LumaComponents.kt、ui/design/LumaDesignSystem.kt、memory/*。 +教训: 模拟器继续禁用 Kyant RenderEffect 采样,截图中的玻璃为安全降级态;真实 Backdrop 仅在设备安全门和帧率门控同时通过时启用。 +标签: #lumaplayer #glass #fallback #dark-mode #verification diff --git a/memory/MEMORY.md b/memory/MEMORY.md index 2104d4d..60cf5a6 100644 --- a/memory/MEMORY.md +++ b/memory/MEMORY.md @@ -8,3 +8,4 @@ - 当前 UI:新增 `ui/design` 令牌、公共加载/空状态/按钮、Aurora 图标子集和授权说明;主要页面已接入统一颜色、间距、圆角和动效。 - 构建验证:Backdrop 层级收敛、模拟器安全降级、播放器采样隔离和 GlassSheet 收尾后 `assembleDebug`、`lintDebug`、`testDebugUnitTest` 成功;已在 `sdk_gphone64_arm64` API 35 模拟器安装启动并点击 Vision/Settings/语言菜单,未出现 `SIGSEGV/RenderThread`,尚未做真机录屏比对。 - 详细状态见 [projects.md](projects.md),踩坑见 [lessons.md](lessons.md)。 +- 最新收尾:公共分段控件、按钮、输入框、主题/语言/资源库弹层统一走同一套降级毛玻璃;状态栏/导航栏图标随深色主题同步;Debug/Lint/Unit 与 API35 模拟器回归通过。 diff --git a/memory/projects.md b/memory/projects.md index 12ee32d..8ff0c57 100644 --- a/memory/projects.md +++ b/memory/projects.md @@ -33,3 +33,4 @@ - 2026-08-27:播放器按 Emby Playback Check-in 约定补齐 `PlaySessionId`、`MediaSourceId`、`PositionTicks`、`CanSeek`、`QueueableMediaTypes` 和事件字段;播放开始/暂停/继续/拖动/播放中进度/停止统一进入串行队列,普通进度按 10 秒或位置变化阈值上报。 - 2026-08-27:首页增加按服务器身份隔离的进程内内容缓存,普通页面返回不重复请求,显式重试才刷新;玻璃运行时增加 Choreographer 帧率门控,底栏高帧率安全设备走本地 AndroidLiquidGlass-kmp 的 Lens,模拟器/低帧率使用连续胶囊毛玻璃和选中高光;`assembleDebug`、`lintDebug`、模拟器安装启动复核成功。 - 2026-08-27:底栏普通图标调整为 33dp、搜索图标 37dp;降级玻璃增加最终 Capsule 裁剪并减弱蓝色选中填充,原生路径空闲态保留轻微 Lens;详情/播放器/剧集加载和播放地址失败时回落到错误态;最终 APK 在 API 35 模拟器启动、Vision/Settings/语言菜单点击复核成功。 +- 2026-08-27:收敛公共分段控件、弹出菜单、按钮、输入框及语言菜单的降级玻璃路径,移除纯色方框;补充深色主题系统栏图标同步和操作标题 48dp 点击区域。`assembleDebug`、`lintDebug`、`testDebugUnitTest` 成功,API 35 模拟器验证设置/语言/主题/关于/服务器添加链路无崩溃;本地参考素材仍未跟踪。