收敛玻璃降级并完善深色系统栏
This commit is contained in:
1 parent
f24286d758
commit
3b322012cf
9 files changed
+69
-49
No files matched your search
@@ -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) }
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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 模拟器回归通过。
|
||||
@@ -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 模拟器验证设置/语言/主题/关于/服务器添加链路无崩溃;本地参考素材仍未跟踪。
|
||||
Reference in new issue
Block a user