收敛降级玻璃与页面状态恢复
This commit is contained in:
1 parent
e76637c562
commit
485538f296
8 files changed
+56
-20
No files matched your search
@@ -77,7 +77,9 @@ fun AppNavHost(
|
||||
if (currentEntry?.destination?.route == AppRoute.MediaDetail.pattern &&
|
||||
currentEntry.arguments?.getString("itemId") == itemId
|
||||
) return@let
|
||||
navController.navigate(AppRoute.MediaDetail.create(serverId, itemId))
|
||||
navController.navigate(AppRoute.MediaDetail.create(serverId, itemId)) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +182,9 @@ fun AppNavHost(
|
||||
val alreadyPlaying = currentEntry?.destination?.route == AppRoute.VideoPlayer.pattern &&
|
||||
currentEntry.arguments?.getString("itemId") == itemId
|
||||
if (!alreadyPlaying) {
|
||||
navController.navigate(AppRoute.VideoPlayer.create(serverId, itemId, seriesId, seasonId))
|
||||
navController.navigate(AppRoute.VideoPlayer.create(serverId, itemId, seriesId, seasonId)) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
@@ -584,6 +585,7 @@ fun HomeScreen(
|
||||
|
||||
val heroItems = remember(latestItems) { latestItems.take(8) }
|
||||
val heroPagerState = rememberPagerState(pageCount = { heroItems.size })
|
||||
val homeListState = rememberLazyListState()
|
||||
val activeHeroItem = heroItems.getOrNull(heroPagerState.currentPage)
|
||||
val immersiveBackground = rememberPosterImmersiveBackground(heroImageUrl(activeHeroItem, activeServer?.fullUrl.orEmpty()))
|
||||
|
||||
@@ -690,7 +692,8 @@ fun HomeScreen(
|
||||
} else {
|
||||
val server = activeServer
|
||||
LazyColumn(
|
||||
Modifier.fillMaxSize(),
|
||||
state = homeListState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(bottom = bottomBarContentPadding(90.dp).calculateBottomPadding())
|
||||
) {
|
||||
if (heroItems.isNotEmpty()) {
|
||||
|
||||
@@ -80,6 +80,7 @@ import com.example.myapplication.data.EmbyServer
|
||||
import com.example.myapplication.R as Res
|
||||
import com.kyant.backdrop.Backdrop
|
||||
import top.yukonga.miuix.kmp.basic.Text
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import java.util.Locale
|
||||
|
||||
private val LocalDetailText = staticCompositionLocalOf { Color.White }
|
||||
@@ -117,22 +118,32 @@ fun MediaDetailScreen(
|
||||
LaunchedEffect(item.id, server?.id, server?.apiKey) {
|
||||
if (server == null || server.apiKey.isBlank()) return@LaunchedEffect
|
||||
refreshing = true
|
||||
val fullItem = EmbyRepository.getItem(server, item.id)
|
||||
if (fullItem == null) {
|
||||
loadError = detailLoadFailed
|
||||
} else {
|
||||
detail = fullItem
|
||||
loadError = null
|
||||
}
|
||||
refreshing = false
|
||||
try {
|
||||
val fullItem = EmbyRepository.getItem(server, item.id)
|
||||
if (fullItem == null) {
|
||||
loadError = detailLoadFailed
|
||||
} else {
|
||||
detail = fullItem
|
||||
loadError = null
|
||||
}
|
||||
|
||||
if (detail.type == "Series") {
|
||||
val loadedSeasons = EmbyRepository.getSeasons(server, item.id)
|
||||
seasons = loadedSeasons
|
||||
selectedSeasonId = loadedSeasons.firstOrNull()?.id
|
||||
} else {
|
||||
seasons = emptyList()
|
||||
selectedSeasonId = null
|
||||
if (detail.type == "Series") {
|
||||
val loadedSeasons = EmbyRepository.getSeasons(server, item.id)
|
||||
seasons = loadedSeasons
|
||||
selectedSeasonId = loadedSeasons
|
||||
.firstOrNull { it.id == selectedSeasonId }
|
||||
?.id
|
||||
?: loadedSeasons.firstOrNull()?.id
|
||||
} else {
|
||||
seasons = emptyList()
|
||||
selectedSeasonId = null
|
||||
}
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (_: Exception) {
|
||||
loadError = detailLoadFailed
|
||||
} finally {
|
||||
refreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +152,13 @@ fun MediaDetailScreen(
|
||||
val seriesId = item.id
|
||||
val seasonId = selectedSeasonId ?: return@LaunchedEffect
|
||||
if (detail.type != "Series" || seasonId.isBlank()) return@LaunchedEffect
|
||||
episodes = EmbyRepository.getEpisodes(currentServer, seriesId, seasonId)
|
||||
episodes = try {
|
||||
EmbyRepository.getEpisodes(currentServer, seriesId, seasonId)
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (_: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
val seriesFallbackImageUrl = EmbyRepository.buildImageUrl(server?.fullUrl.orEmpty(), detail, maxWidth = 640)
|
||||
|
||||
@@ -224,8 +224,11 @@ fun Modifier.lumaFallbackGlass(
|
||||
endY = size.height * 0.72f,
|
||||
)
|
||||
onDrawWithContent {
|
||||
drawContent()
|
||||
// The fallback surface is a material layer. Its highlight must
|
||||
// sit behind the content; drawing it after drawContent makes
|
||||
// labels and icons look washed out on devices without Backdrop.
|
||||
drawRect(highlight)
|
||||
drawContent()
|
||||
}
|
||||
}
|
||||
.then(if (border) Modifier.border(0.6.dp, edge, shape) else Modifier)
|
||||
|
||||
@@ -3,3 +3,9 @@
|
||||
文件变更: MainActivity.kt、AppTheme.kt、LumaDesignSystem.kt、LiquidBottomTabs.kt、AppBottomBar.kt、SettingsScreen.kt、strings.xml、values-en/strings.xml 及既有 UI 收尾文件。
|
||||
教训: 远端有新提交时先暂存跟踪文件再合并;模式手动选择与模拟器安全硬门需同时保留。
|
||||
标签: #lumaplayer #fallback-ui #glass-mode #renderthread #remote-merge
|
||||
|
||||
[PROJECT:lumaplayer] 页面状态与导航收尾
|
||||
结论: 修复降级高光覆盖、首页滚动恢复及详情/播放器重复导航与异常态,构建和 API35 启动回归成功。
|
||||
文件变更: ui/design/LumaDesignSystem.kt、screens/HomeScreen.kt、navigation/AppNavHost.kt、screens/MediaDetailScreen.kt。
|
||||
教训: 页面请求异常需保留取消异常;可保存滚动状态和 launchSingleTop 能减少返回闪烁与重复页面。
|
||||
标签: #lumaplayer #navigation #state-restoration #fallback-ui
|
||||
@@ -10,3 +10,4 @@
|
||||
- 详细状态见 [projects.md](projects.md),踩坑见 [lessons.md](lessons.md)。
|
||||
- 最新收尾:公共分段控件、按钮、输入框、主题/语言/资源库弹层统一走同一套降级毛玻璃;底栏降级选中胶囊改为内容下层绘制;新增玻璃模式持久化设置;状态栏/导航栏图标随深色主题同步;Debug/Lint/Unit 与 API35 模拟器回归通过。
|
||||
- 最新改动:播放器进度条改为自绘可访问 SeekBar,媒体首页、Vision 收藏和搜索加载态改为统一骨架占位;移除未使用的 Material 图标依赖,完整 Debug/Lint/Unit 通过。
|
||||
- 最新校验:修复降级玻璃高光覆盖内容、首页滚动状态恢复与重复详情/播放器导航;Debug/Lint/Unit 和 API35 模拟器启动回归成功。
|
||||
@@ -19,3 +19,4 @@
|
||||
- 中:Emby 播放回报必须沿用 PlaybackInfo 返回的 `PlaySessionId`/`MediaSourceId`,并将开始、进度、停止事件串行化;把回报协程绑定到播放器 Composable 生命周期会在返回页面时丢失停止事件。
|
||||
- 高:液态玻璃不能以“API 版本高”作为唯一启用条件;Kyant Backdrop 还需通过设备安全门和运行帧率门控,低帧率/不安全 GPU 必须在创建 LayerBackdrop 前降级,避免 RenderThread 崩溃。
|
||||
- 高:玻璃模式已改为用户手动选择,不能再用 Choreographer 帧率变化自动切换;但模拟器/低版本仍必须在创建 Kyant LayerBackdrop 前保留硬件安全门,避免 RenderThread SIGSEGV。
|
||||
- 中:降级玻璃的边缘高光应在 `drawContent()` 之前绘制;否则公共玻璃容器中的文字和图标会被高光层洗白。
|
||||
@@ -37,3 +37,4 @@
|
||||
- 2026-08-27:播放器进度条替换为自绘拖拽控件并补充无障碍进度调节,Home/Vision/Search 的媒体加载态改为统一骨架占位;移除未使用的 Material 图标依赖。`assembleDebug`、`lintDebug`、`testDebugUnitTest` 成功。
|
||||
- 2026-08-27:底栏图标调整为 36dp、搜索图标 41dp,资源库无图标服务器使用正确的服务器图标;搜索清除与图标库返回补足 48dp 触控范围。完整 Debug/Lint/Unit 构建和 API35 模拟器回归通过。
|
||||
- 2026-08-28:完整读取本地 `luna/README.txt`、`AI_PROMPT.txt` 与降级参考 contact sheet;修复降级底栏选中胶囊覆盖内容的问题,统一为稳定渐变毛玻璃;移除 Choreographer 帧率自动切换,设置页新增液态玻璃/毛玻璃手动选择并持久化;合并远端 3 个播放器提交后 `assembleDebug`、`lintDebug` 与 API35 模拟器启动回归通过。
|
||||
- 2026-08-28:继续收尾降级 UI;修复 fallback 高光绘制顺序,首页接入可保存滚动状态,详情/播放器导航启用 `launchSingleTop` 防重复 Push,详情刷新和剧集加载异常进入已有错误态;`assembleDebug`、`lintDebug`、`testDebugUnitTest` 及 API35 模拟器启动回归成功。
|
||||
Reference in new issue
Block a user