fix moegirl ad blocker and optimize func

This commit is contained in:
2026-09-26 12:58:12 +08:00
parent e3e346b93a
commit 43e9e1b916
17 changed files with 550 additions and 302 deletions
@@ -0,0 +1,52 @@
package com.zhushenwudi.lovelivemusicplayer.ui.theme
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.os.Build
import android.view.WindowInsets
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@Composable
internal actual fun rememberStartupWindowMetrics(): StartupWindowMetrics {
val view = LocalView.current
return remember(view) { view.context.readStartupWindowMetrics() }
}
private fun Context.readStartupWindowMetrics(): StartupWindowMetrics {
val activity = findActivity() ?: return StartupWindowMetrics()
if (Build.VERSION.SDK_INT >= 30) {
val metrics = activity.windowManager.currentWindowMetrics
val mask = WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()
val bars = metrics.windowInsets.getInsets(mask)
return StartupWindowMetrics(
heightPx = metrics.bounds.height(),
statusTopPx = bars.top,
navBottomPx = bars.bottom,
)
}
val root = ViewCompat.getRootWindowInsets(activity.window.decorView)
val statusTop = root?.getInsets(WindowInsetsCompat.Type.statusBars())?.top
?: activity.statusBarFallbackPx()
val navBottom = root?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
return StartupWindowMetrics(
heightPx = activity.resources.displayMetrics.heightPixels,
statusTopPx = statusTop,
navBottomPx = navBottom,
)
}
private fun Activity.statusBarFallbackPx(): Int {
val id = resources.getIdentifier("status_bar_height", "dimen", "android")
return if (id > 0) resources.getDimensionPixelSize(id) else 0
}
private tailrec fun Context.findActivity(): Activity? = when (this) {
is Activity -> this
is ContextWrapper -> baseContext.findActivity()
else -> null
}
@@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.zhushenwudi.lovelivemusicplayer.AppContainer.scope
import com.zhushenwudi.lovelivemusicplayer.data.DataImportService
import com.zhushenwudi.lovelivemusicplayer.db.LibraryRepository
@@ -148,7 +149,6 @@ object AppContainer {
// Android 通知封面用 file:// / https;iOS Now Playing 优先绝对路径。
player.resolveCoverUri = { music ->
pathResolver.absoluteCoverPath(music)
?.takeIf { fileSystem.exists(it) }
?: pathResolver.coverDisplayUri(music)
}
@@ -181,7 +181,9 @@ object AppContainer {
this.dataSync = DataSyncService(WsClient(http), library, scope)
this.updateChecker = UpdateChecker(ossApi)
this.sleepTimer = SleepTimer(scope) {
player.pause()
withContext(Dispatchers.Main) {
runCatching { player.pause() }
}
}
this.splashPhoto = SplashPhotoService(fileSystem, ossApi, scope)
@@ -195,7 +197,6 @@ object AppContainer {
persistPlayQueue()
}
}
refreshCarPlayCatalog()
PlatformServices.desktopLyric.pipAutoOpen(settings.enableDesktopLyric)
PlatformServices.carPlay.bind(PlatformServices.carPlayCatalog)
bindCarPlayNowPlaying()
@@ -206,6 +207,11 @@ object AppContainer {
bindHistoryOnTrackChange()
initSdStorage()
bindUsbMountListener()
// 全表快照放后台,并避开主页出来后的首屏解码。
scope.launch {
delay(2_000)
refreshCarPlayCatalog()
}
}
/**
@@ -467,6 +473,8 @@ object AppContainer {
private fun bindCarPlayNowPlaying() {
carPlayNowPlayingJob?.cancel()
carPlayNowPlayingJob = scope.launch {
var recentKey: Pair<String?, Boolean>? = null
var skipStartupRecent = true
player.state
.map {
listOf(
@@ -489,11 +497,20 @@ object AppContainer {
favorite = current?.isLove == true,
loopMode = player.state.value.loopMode,
)
val recent = library.findAllListByGroup(
group = "all",
enableHttp = settings.enableHttp,
).recent.withSortOrder(settings.recentSortOrder)
PlatformServices.carPlayCatalog.updateRecent(recent, cover)
val key = current?.musicId to (current?.isLove == true)
if (key != recentKey) {
recentKey = key
// 冷启动这一次交给延后的曲库快照,避免和首屏抢数据库。
if (skipStartupRecent) {
skipStartupRecent = false
} else {
val recent = library.recentVisible(
group = "all",
enableHttp = settings.enableHttp,
).withSortOrder(settings.recentSortOrder)
PlatformServices.carPlayCatalog.updateRecent(recent, cover)
}
}
PlatformServices.carPlay.refreshNowPlaying()
}
}
@@ -48,9 +48,12 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.min
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.rememberHazeState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.zhushenwudi.lovelivemusicplayer.AppContainer
import com.zhushenwudi.lovelivemusicplayer.Const
import com.zhushenwudi.lovelivemusicplayer.domain.Album
@@ -145,8 +148,9 @@ fun MainShell(
val section = if (topTabIndex == 0) TopSection.LIBRARY else TopSection.MINE
val weSlide = AppContainer.weSlide
val weSlideFooter = AppContainer.weSlideFooter
// 迷你播放器毛玻璃的 Haze 背景源
// 迷你播放器毛玻璃的 Haze 背景源。首屏先不挂,避免整页离屏层和封面解码叠在一起。
val miniPlayerHaze = rememberHazeState()
var homeHazeEnabled by remember { mutableStateOf(false) }
// 选择模式 > 0 时锁定歌库/我的标签与 WeSlide 手势
val batchLock = selectMode == LibrarySelectMode.Batch
val detailOpen = libraryDetail != null
@@ -240,20 +244,14 @@ fun MainShell(
}
}
// 存储根/SD 路径变更后修复过期的 existFile(轻量 exists 扫描)。
LaunchedEffect(Unit) {
runCatching {
AppContainer.library.rescanExistFiles(AppContainer.pathResolver)
}
refreshToken++
}
// HTTP 关闭时按本地 existFile 过滤曲库。
// 正倒序按页独立计算,不写进查询 key。
val enableHttp = AppContainer.settings.enableHttp
val historyRevision by AppContainer.historyRevision.collectAsState()
val loveRevision by AppContainer.loveRevision.collectAsState()
val libraryRevision by AppContainer.libraryRevision.collectAsState()
val startupHistoryRevision = remember { historyRevision }
val startupLoveRevision = remember { loveRevision }
val libraryLists = remember(refreshToken, currentGroup, enableHttp, libraryRevision) {
runCatching {
AppContainer.library.findAllListByGroup(
@@ -271,12 +269,12 @@ fun MainShell(
)
}
}
var music by remember { mutableStateOf(emptyList<Music>()) }
var albums by remember { mutableStateOf(emptyList<Album>()) }
var music by remember { mutableStateOf(libraryLists.music) }
var albums by remember { mutableStateOf(libraryLists.albums) }
var artists by remember { mutableStateOf(libraryLists.artists) }
var menus by remember { mutableStateOf(libraryLists.menus) }
var loved by remember { mutableStateOf(emptyList<Music>()) }
var recent by remember { mutableStateOf(emptyList<Music>()) }
var loved by remember { mutableStateOf(libraryLists.loved) }
var recent by remember { mutableStateOf(libraryLists.recent) }
LaunchedEffect(libraryLists) {
music = libraryLists.music
albums = libraryLists.albums
@@ -285,35 +283,64 @@ fun MainShell(
loved = libraryLists.loved
recent = libraryLists.recent
}
// 播放历史更新时仅刷新最近播放列表。
// 首屏封面先解码。本地文件核对延后,避免扫盘占满磁盘把列表拖卡。
LaunchedEffect(Unit) {
delay(1_500)
homeHazeEnabled = true
val groupAtStart = currentGroup.displayName
val httpAtStart = enableHttp
val changed = withContext(Dispatchers.IO) {
runCatching {
AppContainer.library.rescanExistFiles(AppContainer.pathResolver)
}.getOrDefault(0)
}
if (changed > 0 &&
currentGroup.displayName == groupAtStart &&
enableHttp == httpAtStart
) {
val lists = withContext(Dispatchers.Default) {
runCatching {
AppContainer.library.findAllListByGroup(groupAtStart, httpAtStart)
}.getOrNull()
}
if (lists != null &&
currentGroup.displayName == groupAtStart &&
enableHttp == httpAtStart
) {
music = lists.music
albums = lists.albums
artists = lists.artists
menus = lists.menus
loved = lists.loved
recent = lists.recent
}
}
}
// 播放历史更新时仅刷新最近播放列表。启动时已经记过的版本不再查库。
LaunchedEffect(historyRevision, currentGroup, enableHttp) {
if (historyRevision == 0L) return@LaunchedEffect
runCatching {
AppContainer.library.findAllListByGroup(
group = currentGroup.displayName,
enableHttp = enableHttp,
).recent
}.getOrNull()?.let { recent = it }
if (historyRevision == startupHistoryRevision) return@LaunchedEffect
val groupName = currentGroup.displayName
val http = enableHttp
val next = withContext(Dispatchers.Default) {
runCatching {
AppContainer.library.recentVisible(groupName, http)
}.getOrNull()
}
if (next != null) recent = next
}
// 收藏切换时刷新收藏列表。
LaunchedEffect(loveRevision, currentGroup, enableHttp) {
if (loveRevision == 0L) return@LaunchedEffect
runCatching {
AppContainer.library.findAllListByGroup(
group = currentGroup.displayName,
enableHttp = enableHttp,
)
}.getOrNull()?.let { lists ->
loved = lists.loved
// 同步歌曲/最近播放行的 isLove 与 Music.isLove 列。
fun patch(list: List<Music>) = list.map { m ->
val id = m.musicId ?: return@map m
val love = AppContainer.library.getMusicById(id)?.isLove == true
if (m.isLove == love) m else m.copy(isLove = love)
}
music = patch(music)
recent = patch(recent)
}
if (loveRevision == startupLoveRevision) return@LaunchedEffect
val groupName = currentGroup.displayName
val http = enableHttp
val lists = withContext(Dispatchers.Default) {
runCatching {
AppContainer.library.findAllListByGroup(groupName, http)
}.getOrNull()
} ?: return@LaunchedEffect
loved = lists.loved
music = lists.music
recent = lists.recent
}
// 搜索模式:在歌曲/收藏/最近播放中按歌名过滤。
val displayMusic = remember(music, songsSort, searchQuery, selectMode) {
@@ -582,7 +609,7 @@ fun MainShell(
) {
CompositionLocalProvider(
LocalLayoutDirection provides LayoutDirection.Ltr,
LocalMiniPlayerHaze provides miniPlayerHaze,
LocalMiniPlayerHaze provides if (homeHazeEnabled) miniPlayerHaze else null,
LocalHomeListBottomInset provides HomeListChrome.ListBottomInset,
) {
WeSlide(
@@ -617,7 +644,13 @@ fun MainShell(
Box(
Modifier
.fillMaxSize()
.hazeSource(state = miniPlayerHaze),
.then(
if (homeHazeEnabled) {
Modifier.hazeSource(state = miniPlayerHaze)
} else {
Modifier
},
),
) {
Column(
Modifier
@@ -712,7 +745,7 @@ fun MainShell(
state = contentPagerState,
modifier = Modifier.weight(1f).fillMaxWidth(),
verticalAlignment = Alignment.Top,
beyondViewportPageCount = 1,
beyondViewportPageCount = 0,
userScrollEnabled = !weSlide.isOpened &&
!detailOpen &&
selectMode == LibrarySelectMode.Normal,
@@ -1,22 +1,29 @@
package com.zhushenwudi.lovelivemusicplayer.ui.player
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
@@ -27,13 +34,20 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlin.math.roundToInt
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.HazeTint
import dev.chrisbanes.haze.hazeEffect
@@ -48,6 +62,7 @@ import com.zhushenwudi.lovelivemusicplayer.ui.icons.SvgAsset
import com.zhushenwudi.lovelivemusicplayer.ui.icons.TintIcon
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlin.time.Duration.Companion.milliseconds
/** 迷你栏背后的主页列表内容,由 [com.zhushenwudi.lovelivemusicplayer.ui.MainShell] 提供。 */
val LocalMiniPlayerHaze = compositionLocalOf<HazeState?> { null }
@@ -132,15 +147,9 @@ fun MiniPlayerBar(
)
}
Spacer(Modifier.width(8.dp))
// 居中跑马灯歌名
Text(
MiniPlayerTitle(
text = title,
color = titleColor,
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
modifier = Modifier
.weight(1f)
.then(
@@ -205,6 +214,85 @@ fun MiniPlayerBar(
}
}
private val MiniTitleStyle = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
)
/** 略慢于常见 30dp/s 走马灯。 */
private val MiniTitleMarqueeVelocity = 20.dp
private const val MiniTitleMarqueeHoldMs = 2_000
/**
* 歌名宽于显示区域时走马灯:左侧对齐停 2 秒,匀速滚到右侧对齐再停 2 秒,
* 然后跳回起点,再等 2 秒继续。未超出时居中静止。
*/
@Composable
private fun MiniPlayerTitle(
text: String,
color: Color,
modifier: Modifier = Modifier,
) {
val textMeasurer = rememberTextMeasurer()
val style = MiniTitleStyle.copy(color = color)
BoxWithConstraints(
modifier = modifier.clipToBounds(),
contentAlignment = Alignment.CenterStart,
) {
val maxWidthPx = constraints.maxWidth
val density = LocalDensity.current
val textWidthPx = remember(text, style, textMeasurer, maxWidthPx) {
textMeasurer.measure(
text = text,
style = style,
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Visible,
).size.width
}
val overflowPx = (textWidthPx - maxWidthPx).coerceAtLeast(0)
if (overflowPx <= 0) {
Text(
text = text,
style = style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
)
} else {
val offsetX = remember(text) { Animatable(0f) }
LaunchedEffect(text, overflowPx) {
val velocityPxPerSec = with(density) { MiniTitleMarqueeVelocity.toPx() }
val durationMs = (overflowPx / velocityPxPerSec * 1_000f)
.toInt()
.coerceAtLeast(1)
offsetX.snapTo(0f)
while (true) {
delay(MiniTitleMarqueeHoldMs.toLong().milliseconds)
offsetX.animateTo(
targetValue = -overflowPx.toFloat(),
animationSpec = tween(durationMs, easing = LinearEasing),
)
delay(MiniTitleMarqueeHoldMs.toLong().milliseconds)
offsetX.snapTo(0f)
}
}
Text(
text = text,
style = style,
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Visible,
modifier = Modifier
.wrapContentWidth(unbounded = true, align = Alignment.Start)
.offset { IntOffset(offsetX.value.roundToInt(), 0) },
)
}
}
}
@Composable
private fun MiniBarIconButton(
asset: SvgAsset,
@@ -196,7 +196,11 @@ fun SongList(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(bottom = listBottomInset),
) {
items(music) { item ->
items(
items = music,
key = { it.musicId ?: "${it.musicName}-${it.musicPath}-${it.albumId}" },
contentType = { "song" },
) { item ->
val id = item.musicId.orEmpty()
SongListItem(
music = item,
@@ -267,7 +271,11 @@ private fun AlbumList(
),
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
items(albums) { album ->
items(
items = albums,
key = { it.albumId ?: "${it.albumName}-${it.coverPath}" },
contentType = { "album" },
) { album ->
AlbumGridItem(
album = album,
onClick = { album.albumId?.let(onOpen) },
@@ -1,12 +1,19 @@
package com.zhushenwudi.lovelivemusicplayer.ui.theme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.statusBars
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.unit.Density
@@ -24,31 +31,52 @@ val LocalDesignScale = staticCompositionLocalOf { 1f }
/** [com.zhushenwudi.lovelivemusicplayer.ui.theme.ProvideDesignDensity] 缩放前的设备密度(供底部栏使用)。 */
val LocalUnscaledDensity = staticCompositionLocalOf<Density?> { null }
/**
* 首帧 [androidx.compose.ui.platform.LocalWindowInfo] 仍是 0×0、系统栏 inset 也还没派发时,
* 用平台窗口度量顶上,避免先按 scale=1 铺满状态栏再缩回。
*/
data class StartupWindowMetrics(
val heightPx: Int = 0,
val statusTopPx: Int = 0,
val navBottomPx: Int = 0,
)
val LocalStartupWindowMetrics = staticCompositionLocalOf { StartupWindowMetrics() }
@Composable
internal expect fun rememberStartupWindowMetrics(): StartupWindowMetrics
/**
* 按设计稿比例缩放 [androidx.compose.ui.platform.LocalDensity],使 dp/sp 与设计尺寸一致。
* 在应用根节点调用一次;底部栏需保持设备密度时用 [com.zhushenwudi.lovelivemusicplayer.ui.theme.ProvideUnscaledDensity] 包裹。
*/
@Composable
fun ProvideDesignDensity(content: @Composable () -> Unit) {
val base = LocalDensity.current
val containerPx = LocalWindowInfo.current.containerSize
val heightDp = containerPx.height / base.density
// 按高度比例缩放;不低于设计尺寸;平板上限 1.35。忽略系统字体缩放(对齐 Flutter textScaler = 1.0)。
val scale = remember(heightDp) {
(heightDp / Const.UI_HEIGHT).coerceIn(1f, 1.35f)
}
val scaled = remember(base, scale) {
Density(
density = base.density * scale,
fontScale = 1f,
val startup = rememberStartupWindowMetrics()
CompositionLocalProvider(LocalStartupWindowMetrics provides startup) {
val base = LocalDensity.current
val containerPx = LocalWindowInfo.current.containerSize
val heightPx = if (containerPx.height > 0) containerPx.height else startup.heightPx
if (heightPx <= 0) {
Box(Modifier.fillMaxSize())
return@CompositionLocalProvider
}
val heightDp = heightPx / base.density
// 按高度比例缩放;不低于设计尺寸;平板上限 1.35。忽略系统字体缩放(对齐 Flutter textScaler = 1.0)。
val scale = (heightDp / Const.UI_HEIGHT).coerceIn(1f, 1.35f)
val scaled = remember(base.density, scale) {
Density(
density = base.density * scale,
fontScale = 1f,
)
}
CompositionLocalProvider(
LocalDensity provides scaled,
LocalUnscaledDensity provides base,
LocalDesignScale provides scale,
content = content,
)
}
CompositionLocalProvider(
LocalDensity provides scaled,
LocalUnscaledDensity provides base,
LocalDesignScale provides scale,
content = content,
)
}
/** 恢复设备密度,保持底部栏物理尺寸不变。 */
@@ -71,16 +99,38 @@ fun Dp.asUnscaledInScaledContext(): Dp {
/** 状态栏顶部内边距(dp,使用真实设备密度,忽略设计缩放)。 */
@Composable
fun unscaledStatusBarsTop(): Dp {
val density = LocalUnscaledDensity.current ?: LocalDensity.current
return with(density) { WindowInsets.statusBars.getTop(this).toDp() }
}
fun unscaledStatusBarsTop(): Dp = unscaledSystemBarPx(
composePx = WindowInsets.statusBars.getTop(systemBarDensity()),
startupPx = LocalStartupWindowMetrics.current.statusTopPx,
)
/** 导航栏底部内边距(dp,使用真实设备密度,忽略设计缩放)。 */
@Composable
fun unscaledNavigationBarsBottom(): Dp {
val density = LocalUnscaledDensity.current ?: LocalDensity.current
return with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
fun unscaledNavigationBarsBottom(): Dp = unscaledSystemBarPx(
composePx = WindowInsets.navigationBars.getBottom(systemBarDensity()),
startupPx = LocalStartupWindowMetrics.current.navBottomPx,
)
@Composable
private fun systemBarDensity(): Density =
LocalUnscaledDensity.current ?: LocalDensity.current
/**
* Compose 首帧系统栏 inset 常为 0。在它真正派发之前用启动时的窗口度量,
* 派发之后以 Compose 为准(含沉浸式隐藏)。
*/
@Composable
private fun unscaledSystemBarPx(composePx: Int, startupPx: Int): Dp {
var dispatched by remember { mutableStateOf(false) }
SideEffect {
if (composePx > 0) dispatched = true
}
val px = when {
composePx > 0 -> composePx
dispatched -> 0
else -> startupPx
}
return with(systemBarDensity()) { px.toDp() }
}
/**
@@ -0,0 +1,6 @@
package com.zhushenwudi.lovelivemusicplayer.ui.theme
import androidx.compose.runtime.Composable
@Composable
internal actual fun rememberStartupWindowMetrics(): StartupWindowMetrics = StartupWindowMetrics()