完善底栏图标与玻璃降级效果
This commit is contained in:
1 parent
d3ce44e3da
commit
f24286d758
24 files changed
+207
-113
No files matched your search
@@ -43,6 +43,7 @@ import com.example.myapplication.screens.SettingsScreen
|
||||
import com.example.myapplication.screens.SettingsTopBar
|
||||
import com.example.myapplication.screens.VideoPlayerScreen
|
||||
import com.example.myapplication.ui.design.LumaLoading
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import top.yukonga.miuix.kmp.basic.Text
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@@ -244,10 +245,16 @@ private fun MediaDetailDestination(
|
||||
|
||||
LaunchedEffect(server?.id, itemId) {
|
||||
loading = true
|
||||
item = if (server == null || server.apiKey.isBlank() || itemId.isBlank()) {
|
||||
item = try {
|
||||
if (server == null || server.apiKey.isBlank() || itemId.isBlank()) {
|
||||
null
|
||||
} else {
|
||||
EmbyRepository.getItem(server, itemId)
|
||||
}
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
} else {
|
||||
EmbyRepository.getItem(server, itemId)
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
@@ -280,19 +287,31 @@ private fun PlayerDestination(
|
||||
|
||||
LaunchedEffect(server?.id, itemId) {
|
||||
itemLoading = true
|
||||
item = if (server == null || server.apiKey.isBlank() || itemId.isBlank()) {
|
||||
item = try {
|
||||
if (server == null || server.apiKey.isBlank() || itemId.isBlank()) {
|
||||
null
|
||||
} else {
|
||||
EmbyRepository.getItem(server, itemId)
|
||||
}
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
} else {
|
||||
EmbyRepository.getItem(server, itemId)
|
||||
}
|
||||
itemLoading = false
|
||||
}
|
||||
|
||||
LaunchedEffect(server?.id, seriesId, seasonId) {
|
||||
episodes = if (server == null || seriesId.isNullOrBlank() || seasonId.isNullOrBlank()) {
|
||||
episodes = try {
|
||||
if (server == null || seriesId.isNullOrBlank() || seasonId.isNullOrBlank()) {
|
||||
emptyList()
|
||||
} else {
|
||||
EmbyRepository.getEpisodes(server, seriesId, seasonId)
|
||||
}
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (_: Exception) {
|
||||
emptyList()
|
||||
} else {
|
||||
EmbyRepository.getEpisodes(server, seriesId, seasonId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,8 +320,16 @@ private fun PlayerDestination(
|
||||
if (server == null || server.apiKey.isBlank() || target == null) return@LaunchedEffect
|
||||
playbackLoading = true
|
||||
playbackError = null
|
||||
playbackSession = EmbyRepository.resolvePlaybackSession(server, target)
|
||||
playbackUrl = playbackSession?.streamUrl
|
||||
try {
|
||||
playbackSession = EmbyRepository.resolvePlaybackSession(server, target)
|
||||
playbackUrl = playbackSession?.streamUrl
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (_: Exception) {
|
||||
playbackSession = null
|
||||
playbackUrl = null
|
||||
playbackError = playbackUrlFailedMessage
|
||||
}
|
||||
playbackLoading = false
|
||||
if (playbackUrl.isNullOrBlank()) playbackError = playbackUrlFailedMessage
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@ import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -92,7 +90,6 @@ import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
|
||||
@@ -45,7 +45,6 @@ import com.example.myapplication.ui.GlassDialog
|
||||
import com.example.myapplication.ui.GlassSheet
|
||||
import com.example.myapplication.ui.GlassInputField
|
||||
import com.example.myapplication.ui.GlassPopupItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -83,21 +82,6 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.example.myapplication.R as Res
|
||||
import top.yukonga.miuix.kmp.basic.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.GridView
|
||||
import androidx.compose.material.icons.filled.ViewAgenda
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material.icons.outlined.MoreHoriz
|
||||
import androidx.compose.material.icons.outlined.PhotoLibrary
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.Image
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material.icons.outlined.Visibility
|
||||
import androidx.compose.material.icons.outlined.VisibilityOff
|
||||
import com.example.myapplication.data.EmbyServer
|
||||
import com.example.myapplication.data.IconItem
|
||||
import com.example.myapplication.data.IconLibrary
|
||||
@@ -223,7 +207,7 @@ private fun ServerCard(server: EmbyServer, tint: Color, onClick: () -> Unit, onC
|
||||
.clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { onMoreClick() },
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(Icons.Outlined.MoreHoriz, contentDescription = stringResource(Res.string.server_actions), tint = GlassTokens.subtext, modifier = Modifier.size(24.dp))
|
||||
LumaIconView(LumaIcon.More, contentDescription = stringResource(Res.string.server_actions), tint = GlassTokens.subtext, modifier = Modifier.size(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,7 +268,7 @@ private fun ServerGridCard(server: EmbyServer, tint: Color, onClick: () -> Unit,
|
||||
.clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { onMoreClick() },
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(Icons.Outlined.MoreHoriz, contentDescription = stringResource(Res.string.server_actions), tint = GlassTokens.subtext, modifier = Modifier.size(24.dp))
|
||||
LumaIconView(LumaIcon.More, contentDescription = stringResource(Res.string.server_actions), tint = GlassTokens.subtext, modifier = Modifier.size(24.dp))
|
||||
}
|
||||
}
|
||||
Box(Modifier.height(22.dp)) {
|
||||
@@ -1453,7 +1437,7 @@ fun AddServerScreen(
|
||||
.clickable(indication = null, interactionSource = remember { MutableInteractionSource() }, onClick = onDismiss),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(Res.string.back), tint = GlassTokens.text, modifier = Modifier.size(20.dp))
|
||||
LumaIconView(LumaIcon.Back, contentDescription = stringResource(Res.string.back), tint = GlassTokens.text, modifier = Modifier.size(20.dp))
|
||||
}
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Text(stringResource(Res.string.add_server), color = GlassTokens.text, fontSize = 22.sp, fontWeight = FontWeight.Bold)
|
||||
@@ -1618,8 +1602,8 @@ private fun ServerFormContent(
|
||||
onValueChange = { password = it },
|
||||
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
trailingContent = {
|
||||
Icon(
|
||||
imageVector = if (showPassword) Icons.Outlined.VisibilityOff else Icons.Outlined.Visibility,
|
||||
LumaIconView(
|
||||
icon = if (showPassword) LumaIcon.EyeOff else LumaIcon.Eye,
|
||||
contentDescription = stringResource(if (showPassword) Res.string.server_hide_password else Res.string.server_show_password),
|
||||
tint = GlassTokens.subtext,
|
||||
modifier = Modifier.size(24.dp).clickable(
|
||||
|
||||
@@ -28,11 +28,6 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
@@ -378,8 +373,8 @@ fun MediaDetailScreen(
|
||||
.clickable(onClick = onClose),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
LumaIconView(
|
||||
icon = LumaIcon.Back,
|
||||
contentDescription = stringResource(Res.string.back),
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(19.dp),
|
||||
@@ -523,7 +518,7 @@ private fun DetailHeadline(detail: EmbyItem, refreshing: Boolean, errorText: Str
|
||||
Spacer(Modifier.height(9.dp))
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
detail.communityRating?.takeIf { it > 0.0 }?.let { rating ->
|
||||
Icon(imageVector = Icons.Filled.Star, contentDescription = null, tint = Color(0xFFFFD54F), modifier = Modifier.size(14.dp))
|
||||
LumaIconView(LumaIcon.Star, contentDescription = null, tint = Color(0xFFFFD54F), modifier = Modifier.size(14.dp))
|
||||
Spacer(Modifier.width(3.dp))
|
||||
Text(text = String.format("%.1f", rating), color = LocalDetailText.current, fontSize = 12.sp, fontWeight = FontWeight.SemiBold)
|
||||
Spacer(Modifier.width(9.dp))
|
||||
|
||||
@@ -20,11 +20,6 @@ import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -73,7 +68,7 @@ private fun PageTopBar(title: String, onBack: () -> Unit) {
|
||||
.clickable(indication = null, interactionSource = remember { androidx.compose.foundation.interaction.MutableInteractionSource() }, onClick = onBack),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back), tint = LumaColors.text, modifier = Modifier.size(20.dp))
|
||||
LumaIconView(LumaIcon.Back, contentDescription = stringResource(R.string.back), tint = LumaColors.text, modifier = Modifier.size(20.dp))
|
||||
}
|
||||
Spacer(Modifier.size(12.dp))
|
||||
Text(title, color = GlassTokens.text, fontSize = 20.sp, fontWeight = FontWeight.Bold)
|
||||
|
||||
@@ -27,9 +27,6 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ChevronRight
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -276,9 +273,9 @@ private fun SettingRowItem(
|
||||
Text(subtitle, color = LumaColors.textSecondary, fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
Icon(
|
||||
Icons.Outlined.ChevronRight,
|
||||
null,
|
||||
LumaIconView(
|
||||
icon = LumaIcon.ChevronRight,
|
||||
contentDescription = null,
|
||||
tint = LumaColors.textTertiary,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
|
||||
@@ -74,7 +74,7 @@ fun LiquidAppBottomBar(
|
||||
selected = selectedTab == index,
|
||||
contentDescription = stringResource(tab.labelRes),
|
||||
tint = if (isGhostLayer) Color.White else if (selectedTab == index) LumaColors.accent else inactive,
|
||||
modifier = Modifier.size(30.dp),
|
||||
modifier = Modifier.size(33.dp),
|
||||
)
|
||||
Spacer(Modifier.height(3.dp))
|
||||
Text(
|
||||
@@ -136,7 +136,7 @@ fun LiquidAppBottomBar(
|
||||
icon = LumaIcon.Search,
|
||||
contentDescription = null,
|
||||
tint = if (searchSelected) LumaColors.accent else inactive,
|
||||
modifier = Modifier.size(34.dp),
|
||||
modifier = Modifier.size(37.dp),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.string.search),
|
||||
|
||||
@@ -63,7 +63,6 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.CompositingStrategy
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -84,13 +83,11 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.math.roundToInt
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.compose.material.icons.outlined.ChevronRight
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.material3.Icon
|
||||
import top.yukonga.miuix.kmp.basic.Text
|
||||
import com.example.myapplication.ui.design.LumaColors
|
||||
import com.example.myapplication.ui.design.LumaIcon
|
||||
@@ -101,21 +98,6 @@ 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 androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Apps
|
||||
import androidx.compose.material.icons.filled.GridView
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Movie
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Storage
|
||||
import androidx.compose.material.icons.outlined.GridView
|
||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||
import androidx.compose.material.icons.outlined.Home
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.Storage
|
||||
|
||||
internal data class NavTab(
|
||||
val icon: LumaIcon,
|
||||
@@ -509,30 +491,6 @@ fun LiquidGlassPopupOverlay(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GlassPopupItem(
|
||||
text: String,
|
||||
icon: ImageVector? = null,
|
||||
tint: Color = Color(0xFF374151),
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 48.dp)
|
||||
.clickable(indication = null, interactionSource = interactionSource, onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
if (icon != null) {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = tint, modifier = Modifier.size(18.dp))
|
||||
}
|
||||
Text(text = text, color = tint, fontSize = 14.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GlassPopupItem(
|
||||
text: String,
|
||||
|
||||
@@ -231,10 +231,10 @@ fun Modifier.lumaFallbackGlass(
|
||||
if (light) {
|
||||
Brush.horizontalGradient(
|
||||
listOf(
|
||||
LumaColors.accent.copy(alpha = 0.24f),
|
||||
Color.White.copy(alpha = 0.36f),
|
||||
Color.White.copy(alpha = 0.22f),
|
||||
LumaColors.accent.copy(alpha = 0.12f),
|
||||
LumaColors.accent.copy(alpha = 0.10f),
|
||||
Color.White.copy(alpha = 0.40f),
|
||||
Color.White.copy(alpha = 0.30f),
|
||||
LumaColors.accent.copy(alpha = 0.08f),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
@@ -251,14 +251,15 @@ fun Modifier.lumaFallbackGlass(
|
||||
Brush.verticalGradient(listOf(top, bottom))
|
||||
}
|
||||
return this
|
||||
.clip(shape)
|
||||
.background(fill, shape)
|
||||
.then(
|
||||
if (selected) {
|
||||
Modifier.drawWithCache {
|
||||
val leftGlow = Brush.radialGradient(
|
||||
colors = listOf(
|
||||
LumaColors.accent.copy(alpha = if (light) 0.22f else 0.26f),
|
||||
LumaColors.accent.copy(alpha = 0.06f),
|
||||
LumaColors.accent.copy(alpha = if (light) 0.14f else 0.18f),
|
||||
LumaColors.accent.copy(alpha = 0.04f),
|
||||
Color.Transparent,
|
||||
),
|
||||
center = Offset(size.width * 0.14f, size.height * 0.18f),
|
||||
@@ -266,7 +267,7 @@ fun Modifier.lumaFallbackGlass(
|
||||
)
|
||||
val rightGlow = Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color.White.copy(alpha = if (light) 0.22f else 0.12f),
|
||||
Color.White.copy(alpha = if (light) 0.28f else 0.16f),
|
||||
Color.Transparent,
|
||||
),
|
||||
center = Offset(size.width * 0.86f, size.height * 0.22f),
|
||||
@@ -283,6 +284,7 @@ fun Modifier.lumaFallbackGlass(
|
||||
},
|
||||
)
|
||||
.border(0.6.dp, edge, shape)
|
||||
.clip(shape)
|
||||
}
|
||||
|
||||
enum class LumaGlassLevel {
|
||||
|
||||
@@ -89,6 +89,11 @@ enum class LumaIcon(
|
||||
Calendar(R.drawable.aurora_calendar, R.drawable.aurora_calendar_selected),
|
||||
Check(R.drawable.aurora_check, R.drawable.aurora_check_selected),
|
||||
Close(R.drawable.aurora_close, R.drawable.aurora_close_selected),
|
||||
Back(R.drawable.aurora_back, R.drawable.aurora_back_selected),
|
||||
ChevronRight(R.drawable.aurora_chevron_right, R.drawable.aurora_chevron_right_selected),
|
||||
More(R.drawable.aurora_more, R.drawable.aurora_more_selected),
|
||||
EyeOff(R.drawable.aurora_eye_off, R.drawable.aurora_eye_off_selected),
|
||||
Star(R.drawable.aurora_star, R.drawable.aurora_star_selected),
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -51,7 +51,6 @@ import com.kyant.backdrop.shadow.Shadow
|
||||
import com.kyant.shapes.Capsule
|
||||
import com.example.myapplication.ui.design.LumaColors
|
||||
import com.example.myapplication.ui.design.LumaGlassRuntime
|
||||
import com.example.myapplication.ui.design.LumaShapes
|
||||
import com.example.myapplication.ui.design.LumaSpacing
|
||||
import com.example.myapplication.ui.design.lumaFallbackGlass
|
||||
import com.example.myapplication.ui.design.rememberLumaLayerBackdrop
|
||||
@@ -206,7 +205,8 @@ fun LiquidBottomTabs(
|
||||
.then(interactiveHighlight.modifier)
|
||||
.height(64f.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(LumaSpacing.xxs),
|
||||
.padding(LumaSpacing.xxs)
|
||||
.clip(Capsule()),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = {
|
||||
CompositionLocalProvider(
|
||||
@@ -287,10 +287,9 @@ fun LiquidBottomTabs(
|
||||
backdrop = rememberCombinedBackdrop(backdrop, tabsBackdrop!!),
|
||||
shape = { Capsule() },
|
||||
effects = {
|
||||
val progress = dampedDragAnimation.pressProgress
|
||||
lens(
|
||||
10f.dp.toPx() * progress,
|
||||
14f.dp.toPx() * progress,
|
||||
10f.dp.toPx(),
|
||||
14f.dp.toPx(),
|
||||
chromaticAberration = true,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M78,47 H30 L50,27 L45,22 L17,50 L45,78 L50,73 L30,53 H78 Z"
|
||||
android:fillColor="#1C1C1E" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M78,47 H30 L50,27 L45,22 L17,50 L45,78 L50,73 L30,53 H78 Z"
|
||||
android:fillColor="#0A84FF" />
|
||||
</vector>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M36,18 L68,50 L36,82"
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#1C1C1E"
|
||||
android:strokeWidth="7"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M36,18 L68,50 L36,82"
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#0A84FF"
|
||||
android:strokeWidth="7"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M17,50 C28,34 39,29 50,29 C61,29 72,34 83,50 C72,66 61,71 50,71 C39,71 28,66 17,50 Z"
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#1C1C1E"
|
||||
android:strokeWidth="6.4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:pathData="M24,24 L76,76"
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#1C1C1E"
|
||||
android:strokeWidth="6.4"
|
||||
android:strokeLineCap="round" />
|
||||
</vector>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M17,50 C28,34 39,29 50,29 C61,29 72,34 83,50 C72,66 61,71 50,71 C39,71 28,66 17,50 Z"
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#0A84FF"
|
||||
android:strokeWidth="6.4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:pathData="M24,24 L76,76"
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeColor="#0A84FF"
|
||||
android:strokeWidth="6.4"
|
||||
android:strokeLineCap="round" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M22,50 A8,8 0 1,0 38,50 A8,8 0 1,0 22,50 M42,50 A8,8 0 1,0 58,50 A8,8 0 1,0 42,50 M62,50 A8,8 0 1,0 78,50 A8,8 0 1,0 62,50"
|
||||
android:fillColor="#1C1C1E" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M22,50 A8,8 0 1,0 38,50 A8,8 0 1,0 22,50 M42,50 A8,8 0 1,0 58,50 A8,8 0 1,0 42,50 M62,50 A8,8 0 1,0 78,50 A8,8 0 1,0 62,50"
|
||||
android:fillColor="#0A84FF" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M50,12 L61,37 L88,40 L67,58 L73,86 L50,71 L27,86 L33,58 L12,40 L39,37 Z"
|
||||
android:fillColor="#1C1C1E" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M50,12 L61,37 L88,40 L67,58 L73,86 L50,71 L27,86 L33,58 L12,40 L39,37 Z"
|
||||
android:fillColor="#0A84FF" />
|
||||
</vector>
|
||||
@@ -112,3 +112,9 @@
|
||||
文件变更: data/EmbyApiModels.kt、data/EmbyRepository.kt、navigation/AppNavHost.kt、screens/VideoPlayerScreen.kt、screens/HomeScreen.kt、ui/design/LumaDesignSystem.kt、ui/AppBottomBar.kt、ui/liquidglass/LiquidBottomTabs.kt、ui/liquidglass/LiquidBottomTab.kt、memory/*。
|
||||
教训: 模拟器不能安全采样 Kyant Backdrop,降级态需用连续胶囊渐变和局部高光保持玻璃层次;提交时排除本地参考仓库和媒体素材。
|
||||
标签: #lumaplayer #emby #playback #cache #liquid-glass #renderthread
|
||||
|
||||
[PROJECT:lumaplayer] 底栏图标与安全降级玻璃收尾
|
||||
结论: 底栏图标放大约 10%,降级玻璃固定最终 Capsule 裁剪并采用白色透光、弱蓝色选中层;原生 AndroidLiquidGlass-kmp 路径空闲态保留轻微 Lens,详情/播放器请求失败不再卡加载;Debug/Lint/Unit 均成功,API 35 模拟器启动和导航点击无崩溃。
|
||||
文件变更: 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
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
|
||||
- 项目:lumaplayer,Android Kotlin/Compose 媒体库客户端。
|
||||
- 当前 Git 分支:`master`。
|
||||
- 最近同步:远端与本地基线均为 `5df288b`,当前工作区有 UI 设计改动。
|
||||
- 最近同步:`master` 已完成底栏图标与安全降级玻璃收尾,提交前工作区仅保留本地参考素材未跟踪。
|
||||
- 当前实现:底栏与安全的 Glass 组件使用 Kyant0 Backdrop 2.0.1;安全且实测帧率不低于 48fps 时使用 Backdrop + blur/vibrancy/lens,模拟器、低帧率或低版本使用渐变毛玻璃降级;页面捕获层仅服务根 Tab/底栏,子页玻璃无源时安全降级;添加服务器为独立普通表单页面并支持 HTTP/HTTPS;页面导航使用 Navigation Compose。
|
||||
- 最新稳定性策略:通过 `LumaGlassRuntime` 识别常见 Android Emulator GPU,模拟器跳过 Kyant RenderEffect/GraphicsLayer 采样,真机保留 Backdrop;播放器不捕获 AndroidView。
|
||||
- 当前 UI:新增 `ui/design` 令牌、公共加载/空状态/按钮、Aurora 图标子集和授权说明;主要页面已接入统一颜色、间距、圆角和动效。
|
||||
- 构建验证:Backdrop 层级收敛、模拟器安全降级、播放器采样隔离和 GlassSheet 收尾后 `assembleDebug`、`lintDebug`、`testDebugUnitTest` 成功;已在 `sdk_gphone64_arm64` 模拟器安装启动复核,未出现 `SIGSEGV/RenderThread`,尚未做真机录屏比对。
|
||||
- 构建验证:Backdrop 层级收敛、模拟器安全降级、播放器采样隔离和 GlassSheet 收尾后 `assembleDebug`、`lintDebug`、`testDebugUnitTest` 成功;已在 `sdk_gphone64_arm64` API 35 模拟器安装启动并点击 Vision/Settings/语言菜单,未出现 `SIGSEGV/RenderThread`,尚未做真机录屏比对。
|
||||
- 详细状态见 [projects.md](projects.md),踩坑见 [lessons.md](lessons.md)。
|
||||
@@ -32,3 +32,4 @@
|
||||
- 2026-08-27:再次加强模拟器识别,增加 x86 ABI、board 和 emu 设备名拦截;详情返回按钮统一为 Luma glass,详情操作目标补足 48dp;`assembleDebug`、`lintDebug`、`testDebugUnitTest` 成功。
|
||||
- 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/语言菜单点击复核成功。
|
||||
Reference in new issue
Block a user