代码优化
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,7 @@ import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import splitties.init.appCtx
|
||||
import splitties.systemservices.notificationManager
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -69,7 +70,7 @@ class CacheBookService : BaseService() {
|
||||
private val downloadJobLock = Any()
|
||||
private var downloadJob: Job? = null
|
||||
private var notificationContent = appCtx.getString(R.string.service_starting)
|
||||
private var mutex = Mutex()
|
||||
private val bookLocks = ConcurrentHashMap<String, Mutex>()
|
||||
private var lastDiagnosticsLogTime = 0L
|
||||
private val notificationBuilder by lazy {
|
||||
val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload)
|
||||
@@ -297,7 +298,8 @@ class CacheBookService : BaseService() {
|
||||
|
||||
if (chapterCount == 0) {
|
||||
cacheBook.setLoading()
|
||||
mutex.withLock {
|
||||
val bookMutex = bookLocks.getOrPut(request.bookUrl) { Mutex() }
|
||||
bookMutex.withLock {
|
||||
val name = book.name
|
||||
if (!admission.isCurrent()) {
|
||||
CacheBook.removeModelFromService(request.bookUrl, cacheBook)
|
||||
@@ -371,8 +373,8 @@ class CacheBookService : BaseService() {
|
||||
}
|
||||
|
||||
private fun admittedBookUrls(): Set<String> {
|
||||
return CacheBook.cacheBookMap.keys.toHashSet().apply {
|
||||
synchronized(admissionLock) {
|
||||
return synchronized(admissionLock) {
|
||||
CacheBook.cacheBookMap.keys.toHashSet().apply {
|
||||
addAll(admittingBookUrls)
|
||||
}
|
||||
}
|
||||
@@ -431,21 +433,24 @@ class CacheBookService : BaseService() {
|
||||
}
|
||||
|
||||
private fun finishAdmissionJob(bookUrl: String) {
|
||||
var restart = false
|
||||
val waiters = synchronized(admissionLock) {
|
||||
val buffer = admissionBuffers[bookUrl]
|
||||
if (buffer != null && buffer.isNotEmpty()) {
|
||||
restart = true
|
||||
emptyList()
|
||||
} else {
|
||||
admissionBuffers.remove(bookUrl)
|
||||
admittingBookUrls.remove(bookUrl)
|
||||
admissionIdleWaiters.remove(bookUrl).orEmpty()
|
||||
// New requests arrived while job was finishing — restart immediately.
|
||||
startAdmissionJob(bookUrl)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (restart) {
|
||||
startAdmissionJob(bookUrl)
|
||||
return
|
||||
admissionBuffers.remove(bookUrl)
|
||||
admittingBookUrls.remove(bookUrl)
|
||||
// Re-check: a concurrent submitDownloadRequest may have added a request
|
||||
// between the buffer empty-check and the admittingBookUrls removal.
|
||||
val recheck = admissionBuffers[bookUrl]
|
||||
if (recheck != null && recheck.isNotEmpty()) {
|
||||
admittingBookUrls.add(bookUrl)
|
||||
startAdmissionJob(bookUrl)
|
||||
return
|
||||
}
|
||||
admissionIdleWaiters.remove(bookUrl).orEmpty()
|
||||
}
|
||||
waiters.forEach { it.complete(Unit) }
|
||||
}
|
||||
@@ -480,26 +485,23 @@ class CacheBookService : BaseService() {
|
||||
|
||||
private suspend fun runDownloadLoop() {
|
||||
try {
|
||||
var lastActiveTime = System.currentTimeMillis()
|
||||
var idleSince = -1L
|
||||
while (currentCoroutineContext().isActive) {
|
||||
drainPendingDownloadRequests()
|
||||
if (CacheBook.isGloballyPaused) {
|
||||
break
|
||||
}
|
||||
val isActiveNow = CacheBook.isRun
|
||||
|| hasPendingDownloadRequests()
|
||||
|| hasAdmittingRequests()
|
||||
if (isActiveNow) {
|
||||
lastActiveTime = System.currentTimeMillis()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
if (!CacheBook.isRun) {
|
||||
if (System.currentTimeMillis() - lastActiveTime > MAX_IDLE_SPIN_MS) break
|
||||
delay(200)
|
||||
if (CacheBook.isRun) {
|
||||
idleSince = -1L
|
||||
CacheBook.startProcessJob(cachePool)
|
||||
continue
|
||||
}
|
||||
CacheBook.startProcessJob(cachePool)
|
||||
// !isRun — only keep looping while there is pending/admitting work.
|
||||
if (!hasPendingDownloadRequests() && !hasAdmittingRequests()) break
|
||||
val now = System.currentTimeMillis()
|
||||
if (idleSince < 0) idleSince = now
|
||||
if (now - idleSince > MAX_IDLE_SPIN_MS) break
|
||||
delay(200)
|
||||
}
|
||||
} finally {
|
||||
val finishedJob = currentCoroutineContext()[Job]
|
||||
|
||||
@@ -136,7 +136,9 @@ fun BookInfoScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class,
|
||||
ExperimentalMaterial3ExpressiveApi::class
|
||||
)
|
||||
@Composable
|
||||
private fun BookInfoScreenContent(
|
||||
state: BookInfoUiState,
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.legado.app.ui.config.themeManage
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -29,6 +30,7 @@ import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.help.config.ThemeExportData
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
@@ -74,7 +76,8 @@ fun EditThemeSheet(
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.navigationBarsPadding()
|
||||
.padding(bottom = 32.dp)
|
||||
.padding(bottom = 32.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
// Name
|
||||
AppTextField(
|
||||
@@ -84,7 +87,6 @@ fun EditThemeSheet(
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
@@ -5,18 +5,21 @@ import android.os.Looper
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
@@ -119,14 +122,17 @@ fun ThemeManageScreen(
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(2),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = adaptiveContentPadding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = 120.dp
|
||||
)
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
item {
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
SplicedColumnGroup {
|
||||
ClickableSettingItem(
|
||||
title = "保存当前设置",
|
||||
@@ -155,12 +161,12 @@ fun ThemeManageScreen(
|
||||
}
|
||||
|
||||
if (savedThemes.isNotEmpty()) {
|
||||
item {
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
AppText(
|
||||
text = "已保存的主题",
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 8.dp)
|
||||
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -288,79 +294,112 @@ private fun SavedThemeItem(
|
||||
) {
|
||||
GlassCard(
|
||||
onClick = onApply,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
cornerRadius = 20.dp
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 8.dp, top = 12.dp, bottom = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
val previewColor = if (theme.data.themeColor != 0) {
|
||||
Color(theme.data.themeColor)
|
||||
} else if (theme.data.cPrimary != 0) {
|
||||
Color(theme.data.cPrimary)
|
||||
} else {
|
||||
MaterialTheme.colorScheme.primary
|
||||
val lightPrimary = if (theme.data.themeColor != 0) Color(theme.data.themeColor)
|
||||
else if (theme.data.cPrimary != 0) Color(theme.data.cPrimary)
|
||||
else MaterialTheme.colorScheme.primary
|
||||
|
||||
val darkPrimary = if (theme.data.cNPrimary != 0) Color(theme.data.cNPrimary)
|
||||
else lightPrimary
|
||||
|
||||
val lightBg = if (theme.data.themeBackgroundColor != 0) Color(theme.data.themeBackgroundColor)
|
||||
else Color(0xFFF7F2FA)
|
||||
|
||||
val darkBg = if (theme.data.isPureBlack) Color.Black else Color(0xFF1C1B1F)
|
||||
|
||||
// 预览区域
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1.2f)
|
||||
) {
|
||||
// 日间行
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
Brush.horizontalGradient(
|
||||
0.5f to lightBg,
|
||||
0.8f to lightPrimary
|
||||
)
|
||||
)
|
||||
) {
|
||||
AppText(
|
||||
text = "日间",
|
||||
style = MaterialTheme.typography.labelMediumEmphasized,
|
||||
color = if (theme.data.primaryTextColor != 0) Color(theme.data.primaryTextColor).copy(alpha = 0.6f)
|
||||
else Color.Black.copy(alpha = 0.5f),
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(12.dp)
|
||||
)
|
||||
}
|
||||
|
||||
// 夜间行
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
Brush.horizontalGradient(
|
||||
0.5f to darkBg,
|
||||
0.8f to darkPrimary
|
||||
)
|
||||
)
|
||||
) {
|
||||
AppText(
|
||||
text = "夜间",
|
||||
style = MaterialTheme.typography.labelMediumEmphasized,
|
||||
color = Color.White.copy(alpha = 0.5f),
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(previewColor)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
) {
|
||||
AppText(
|
||||
text = theme.name,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
maxLines = 1
|
||||
)
|
||||
val features = mutableListOf<String>()
|
||||
features.add(
|
||||
when (theme.data.appTheme) {
|
||||
"0" -> "动态取色"
|
||||
"12" -> "自定义颜色"
|
||||
else -> "预设主题"
|
||||
}
|
||||
)
|
||||
if (theme.data.enableBlur) features.add("模糊效果")
|
||||
if (theme.data.useFloatingBottomBar) features.add("浮动底栏")
|
||||
AppText(
|
||||
text = features.joinToString(" · "),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = onEdit) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = "编辑",
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onExport) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
contentDescription = "导出",
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = "删除",
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(onClick = onEdit, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = "编辑",
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onExport, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
contentDescription = "导出",
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onDelete, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = "删除",
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -240,6 +241,7 @@ fun MainScreen(
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
WideNavigationRailItem(
|
||||
modifier = Modifier.testTag("nav_${destination.route}"),
|
||||
railExpanded = navState.targetValue == WideNavigationRailValue.Expanded,
|
||||
selected = selected,
|
||||
onClick = {
|
||||
@@ -308,6 +310,7 @@ fun MainScreen(
|
||||
MainDestination.My -> ThemeConfig.navIconMy
|
||||
}
|
||||
AppNavigationBarItem(
|
||||
modifier = Modifier.testTag("nav_${destination.route}"),
|
||||
selected = selected,
|
||||
onClick = {
|
||||
coroutineScope.launch { pagerState.animateScrollToPage(index) }
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||
import androidx.activity.compose.ReportDrawnWhen
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
@@ -81,6 +82,7 @@ import androidx.compose.ui.platform.ClipEntry
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
@@ -145,6 +147,8 @@ fun BookshelfScreen(
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
ReportDrawnWhen { uiState.groups.isNotEmpty() }
|
||||
|
||||
val activeOverlay = uiState.activeOverlay
|
||||
val showGroupMenu = activeOverlay == BookshelfOverlay.GroupMenu
|
||||
val isEditMode = uiState.isEditMode
|
||||
@@ -1215,6 +1219,7 @@ fun BookshelfPage(
|
||||
state = gridState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.testTag("bookshelf_list")
|
||||
.then(
|
||||
with(sharedTransitionScope) {
|
||||
if (this != null) Modifier.skipToLookaheadSize() else Modifier
|
||||
|
||||
Reference in New Issue
Block a user