[修复] 修复Compose界面的背景图片显示,添加了一些界面设置选项进Compose设置页中
This commit is contained in:
@@ -3,15 +3,14 @@ package io.legado.app.base
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.view.WindowCompat
|
||||
import io.legado.app.constant.EventBus
|
||||
import io.legado.app.constant.Theme
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.OldThemeConfig
|
||||
import io.legado.app.ui.theme.AppTheme
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.utils.disableAutoFill
|
||||
import io.legado.app.utils.fullScreen
|
||||
import io.legado.app.utils.observeEvent
|
||||
@@ -40,9 +39,7 @@ abstract class BaseComposeActivity(
|
||||
// Compose 入口
|
||||
setContent {
|
||||
AppTheme {
|
||||
Surface() {
|
||||
Content()
|
||||
}
|
||||
Content()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import io.legado.app.ui.book.toc.rule.TxtTocRuleViewModel
|
||||
import io.legado.app.ui.config.coverConfig.CoverConfigViewModel
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfigViewModel
|
||||
import io.legado.app.ui.config.readConfig.ReadConfigViewModel
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfigViewModel
|
||||
import io.legado.app.ui.dict.rule.DictRuleViewModel
|
||||
import io.legado.app.ui.main.MainViewModel
|
||||
import io.legado.app.ui.main.bookshelf.BookshelfViewModel
|
||||
@@ -92,6 +93,7 @@ val appModule = module {
|
||||
viewModelOf(::OtherConfigViewModel)
|
||||
viewModelOf(::ReadConfigViewModel)
|
||||
viewModelOf(::CoverConfigViewModel)
|
||||
viewModelOf(::ThemeConfigViewModel)
|
||||
viewModelOf(::TocViewModel)
|
||||
viewModelOf(::RemoteBookViewModel)
|
||||
viewModelOf(::BookInfoViewModel)
|
||||
|
||||
@@ -17,7 +17,6 @@ import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -32,6 +31,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.AppConst.appInfo
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
@@ -55,7 +55,7 @@ fun AboutScreen(
|
||||
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
@@ -188,4 +188,4 @@ fun AboutScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -40,6 +39,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import io.legado.app.data.entities.Bookmark
|
||||
import io.legado.app.ui.widget.CollapsibleHeader
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
@@ -93,7 +93,7 @@ fun AllBookmarkScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
Column {
|
||||
@@ -260,4 +260,4 @@ fun AllBookmarkScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
@@ -83,6 +82,7 @@ import io.legado.app.model.BookShelfState
|
||||
import io.legado.app.ui.theme.responsiveHazeEffect
|
||||
import io.legado.app.ui.theme.responsiveHazeSource
|
||||
import io.legado.app.ui.widget.components.AnimatedTextLine
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
@@ -286,7 +286,7 @@ fun ExploreShowScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
@@ -762,4 +762,4 @@ fun LoadMoreFooter(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -47,6 +46,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.book.changecover.ChangeCoverDialog
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.button.TopBarButtonVariant
|
||||
@@ -67,7 +67,7 @@ fun BookInfoEditScreen(
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
@@ -278,4 +278,4 @@ fun BookTypeDropdown(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Surface
|
||||
@@ -80,6 +79,7 @@ import cn.hutool.core.date.DateUtil
|
||||
import io.legado.app.data.entities.readRecord.ReadRecord
|
||||
import io.legado.app.data.entities.readRecord.ReadRecordDetail
|
||||
import io.legado.app.ui.widget.components.AnimatedTextLine
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
@@ -142,7 +142,7 @@ fun ReadRecordScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
topBar = {
|
||||
@@ -1019,4 +1019,4 @@ fun formatDuring(mss: Long): String {
|
||||
val m = if (minutes > 0) "${minutes}分钟" else ""
|
||||
val s = if (seconds > 0) "${seconds}秒" else ""
|
||||
return if ("$d$h$m$s".isBlank()) "0秒" else "$d$h$m$s"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.PlainTooltip
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TooltipAnchorPosition
|
||||
import androidx.compose.material3.TooltipBox
|
||||
@@ -66,6 +65,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.data.entities.SearchContentHistory
|
||||
import io.legado.app.ui.widget.components.AnimatedText
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
@@ -135,7 +135,7 @@ fun SearchContentScreen(
|
||||
else -> null
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
Column {
|
||||
@@ -465,4 +465,4 @@ fun SearchResultItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.PrimaryScrollableTabRow
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
@@ -98,6 +97,7 @@ import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity
|
||||
import io.legado.app.ui.replace.ReplaceEditRoute
|
||||
import io.legado.app.ui.widget.CollapsibleHeader
|
||||
import io.legado.app.ui.widget.components.ActionItem
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SelectionBottomBar
|
||||
@@ -295,7 +295,7 @@ fun TocScreen(
|
||||
viewModel.clearSelection()
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
DynamicTopAppBar(
|
||||
@@ -948,4 +948,4 @@ private fun StatusIcon(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,11 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.FileOpen
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
@@ -50,11 +51,11 @@ import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheetMode
|
||||
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
|
||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
import io.legado.app.ui.widget.components.rules.RuleEditFields
|
||||
import io.legado.app.ui.widget.components.rules.RuleEditSheet
|
||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||
@@ -84,9 +85,10 @@ fun TxtRuleScreen(
|
||||
var showEditSheet by remember { mutableStateOf(false) }
|
||||
var editingRule by remember { mutableStateOf<TxtTocRule?>(null) }
|
||||
var showDeleteRuleDialog by remember { mutableStateOf<TxtTocRule?>(null) }
|
||||
|
||||
var showUrlInput by remember { mutableStateOf(false) }
|
||||
var showFilePickerSheet by remember { mutableStateOf(false) }
|
||||
var filePickerMode by remember { mutableStateOf(FilePickerSheetMode.EXPORT) }
|
||||
var showImportSheet by remember { mutableStateOf(false) }
|
||||
var showExportSheet by remember { mutableStateOf(false) }
|
||||
|
||||
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
||||
viewModel.moveItemInList(from.index, to.index)
|
||||
@@ -152,21 +154,38 @@ fun TxtRuleScreen(
|
||||
)
|
||||
}
|
||||
|
||||
if (showFilePickerSheet) {
|
||||
if (showExportSheet) {
|
||||
FilePickerSheet(
|
||||
onDismissRequest = { showFilePickerSheet = false },
|
||||
onDismissRequest = { showExportSheet = false },
|
||||
title = stringResource(R.string.export),
|
||||
onSelectSysDir = {
|
||||
showFilePickerSheet = false
|
||||
showExportSheet = false
|
||||
exportDoc.launch("exportDictRule.json")
|
||||
},
|
||||
onUpload = {
|
||||
showFilePickerSheet = false
|
||||
showExportSheet = false
|
||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||
},
|
||||
allowExtensions = arrayOf("json")
|
||||
)
|
||||
}
|
||||
|
||||
if (showImportSheet) {
|
||||
FilePickerSheet(
|
||||
onDismissRequest = { showImportSheet = false },
|
||||
title = stringResource(R.string.import_txt_toc_rule),
|
||||
onSelectSysFile = { types ->
|
||||
importDoc.launch(types)
|
||||
showImportSheet = false
|
||||
},
|
||||
onManualInput = {
|
||||
showUrlInput = true
|
||||
showImportSheet = false
|
||||
},
|
||||
allowExtensions = arrayOf("json", "txt")
|
||||
)
|
||||
}
|
||||
|
||||
(importState as? BaseImportUiState.Success<TxtTocRule>)?.let { state ->
|
||||
BatchImportDialog(
|
||||
title = "导入词典规则",
|
||||
@@ -279,7 +298,7 @@ fun TxtRuleScreen(
|
||||
}),
|
||||
ActionItem(
|
||||
text = stringResource(R.string.export),
|
||||
onClick = { showFilePickerSheet = true })
|
||||
onClick = { showExportSheet = true })
|
||||
),
|
||||
onDeleteSelected = { ids ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -292,19 +311,11 @@ fun TxtRuleScreen(
|
||||
},
|
||||
snackbarHostState = snackbarHostState,
|
||||
dropDownMenuContent = { dismiss ->
|
||||
DropdownMenuItem(
|
||||
text = { Text("在线导入") },
|
||||
onClick = { dismiss(); showUrlInput = true })
|
||||
DropdownMenuItem(
|
||||
text = { Text("本地导入") },
|
||||
onClick = {
|
||||
dismiss(); importDoc.launch(
|
||||
arrayOf(
|
||||
"text/plain",
|
||||
"application/json"
|
||||
)
|
||||
)
|
||||
})
|
||||
RoundDropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.import_str)) },
|
||||
onClick = { showImportSheet = true; dismiss() },
|
||||
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Box(
|
||||
|
||||
@@ -53,11 +53,16 @@ fun <T> prefDelegate(
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun readInitialValue(): T {
|
||||
return when (defaultValue) {
|
||||
is String -> appCtx.getPrefString(key, defaultValue) as T
|
||||
is Int -> appCtx.getPrefInt(key, defaultValue) as T
|
||||
is Boolean -> appCtx.getPrefBoolean(key, defaultValue) as T
|
||||
is Long -> appCtx.getPrefLong(key, defaultValue) as T
|
||||
val prefs = appCtx.defaultSharedPreferences
|
||||
return when {
|
||||
defaultValue is String -> appCtx.getPrefString(key, defaultValue) as T
|
||||
defaultValue == null && prefs.contains(key) -> {
|
||||
appCtx.getPrefString(key, null) as T
|
||||
}
|
||||
|
||||
defaultValue is Int -> appCtx.getPrefInt(key, defaultValue) as T
|
||||
defaultValue is Boolean -> appCtx.getPrefBoolean(key, defaultValue) as T
|
||||
defaultValue is Long -> appCtx.getPrefLong(key, defaultValue) as T
|
||||
else -> defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -21,13 +20,13 @@ import io.legado.app.base.BaseComposeActivity
|
||||
import io.legado.app.ui.config.coverConfig.CoverConfigScreen
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfigScreen
|
||||
import io.legado.app.ui.config.readConfig.ReadConfigScreen
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
|
||||
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
||||
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
class TestConfigActivity : BaseComposeActivity() {
|
||||
@@ -44,6 +43,9 @@ class TestConfigActivity : BaseComposeActivity() {
|
||||
@Serializable
|
||||
object CoverConfigRoute
|
||||
|
||||
@Serializable
|
||||
object ThemeConfigRoute
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navController = rememberNavController()
|
||||
@@ -57,7 +59,8 @@ class TestConfigActivity : BaseComposeActivity() {
|
||||
onBackClick = { finish() },
|
||||
onNavigateToOther = { navController.navigate(OtherConfigRoute) },
|
||||
onNavigateToRead = { navController.navigate(ReadConfigRoute) },
|
||||
onNavigateToCover = { navController.navigate(CoverConfigRoute) }
|
||||
onNavigateToCover = { navController.navigate(CoverConfigRoute) },
|
||||
onNavigateToTheme = { navController.navigate(ThemeConfigRoute) }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,6 +75,10 @@ class TestConfigActivity : BaseComposeActivity() {
|
||||
composable<CoverConfigRoute> {
|
||||
CoverConfigScreen(onBackClick = { navController.popBackStack() })
|
||||
}
|
||||
|
||||
composable<ThemeConfigRoute> {
|
||||
ThemeConfigScreen(onBackClick = { navController.popBackStack() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +88,12 @@ class TestConfigActivity : BaseComposeActivity() {
|
||||
onBackClick: () -> Unit,
|
||||
onNavigateToOther: () -> Unit,
|
||||
onNavigateToRead: () -> Unit,
|
||||
onNavigateToCover: () -> Unit
|
||||
onNavigateToCover: () -> Unit,
|
||||
onNavigateToTheme: () -> Unit
|
||||
) {
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
@@ -105,10 +113,9 @@ class TestConfigActivity : BaseComposeActivity() {
|
||||
.padding(16.dp)
|
||||
) {
|
||||
SplicedColumnGroup {
|
||||
SwitchSettingItem(
|
||||
title = "使用折叠应用栏",
|
||||
checked = ThemeConfig.useFlexibleTopAppBar,
|
||||
onCheckedChange = { ThemeConfig.useFlexibleTopAppBar = it }
|
||||
ClickableSettingItem(
|
||||
title = stringResource(R.string.theme_setting),
|
||||
onClick = onNavigateToTheme
|
||||
)
|
||||
ClickableSettingItem(
|
||||
title = stringResource(R.string.other_setting),
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -27,6 +26,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
@@ -48,7 +48,7 @@ fun CoverConfigScreen(
|
||||
var manageKey by remember { mutableStateOf<String?>(null) }
|
||||
var showColorPickerByField by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
@@ -282,4 +282,4 @@ fun CoverConfigScreen(
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
@@ -30,6 +29,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.service.WebService
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
@@ -79,7 +79,7 @@ fun OtherConfigScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
@@ -505,4 +505,4 @@ fun ConfirmDialog(
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -20,6 +19,7 @@ import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
@@ -41,7 +41,7 @@ fun ReadConfigScreen(
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
var showPageKeySheet by remember { mutableStateOf(false) }
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
@@ -326,4 +326,4 @@ fun ReadConfigScreen(
|
||||
if (showPageKeySheet) {
|
||||
PageKeySheet(onDismissRequest = { showPageKeySheet = false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package io.legado.app.ui.config.themeConfig
|
||||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun BackgroundImageManageSheet(
|
||||
preferenceKey: String,
|
||||
isDarkTheme: Boolean,
|
||||
onDismissRequest: () -> Unit,
|
||||
viewModel: ThemeConfigViewModel = koinViewModel()
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
var showFilePicker by remember { mutableStateOf(false) }
|
||||
|
||||
val selectImage =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
uri?.let {
|
||||
scope.launch {
|
||||
viewModel.setBackgroundFromUri(
|
||||
uri = it,
|
||||
preferenceKey = preferenceKey
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val currentPath = if (isDarkTheme) {
|
||||
ThemeConfig.bgImageDark
|
||||
} else {
|
||||
ThemeConfig.bgImageLight
|
||||
}
|
||||
|
||||
GlassModalBottomSheet(
|
||||
onDismissRequest = onDismissRequest
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 24.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.background_image),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
|
||||
if (currentPath.isNullOrBlank()) {
|
||||
Surface(
|
||||
onClick = { showFilePicker = true },
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(16f / 9f)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = "Add",
|
||||
modifier = Modifier.size(48.dp),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
tonalElevation = 2.dp
|
||||
) {
|
||||
AsyncImage(
|
||||
model = currentPath,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(9f / 16f),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { viewModel.removeBackground(preferenceKey) },
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(8.dp)
|
||||
.size(32.dp)
|
||||
) {
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.7f)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = "Remove",
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showFilePicker) {
|
||||
FilePickerSheet(
|
||||
onDismissRequest = { showFilePicker = false },
|
||||
onSelectSysFile = {
|
||||
selectImage.launch("image/*")
|
||||
showFilePicker = false
|
||||
},
|
||||
allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package io.legado.app.ui.config.themeConfig
|
||||
|
||||
import android.util.Log
|
||||
import io.legado.app.constant.EventBus
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.ui.config.prefDelegate
|
||||
import io.legado.app.utils.postEvent
|
||||
|
||||
object ThemeConfig {
|
||||
|
||||
@@ -19,16 +22,34 @@ object ThemeConfig {
|
||||
|
||||
var isPureBlack by prefDelegate(PreferKey.pureBlack, false)
|
||||
|
||||
var bgImageLight by prefDelegate<String?>(PreferKey.bgImage, null)
|
||||
var bgImageLight by prefDelegate<String?>(PreferKey.bgImage, null) {
|
||||
postEvent(EventBus.RECREATE, false)
|
||||
}
|
||||
|
||||
var bgImageDark by prefDelegate<String?>(PreferKey.bgImageN, null)
|
||||
var bgImageDark by prefDelegate<String?>(PreferKey.bgImageN, null) {
|
||||
postEvent(EventBus.RECREATE, false)
|
||||
}
|
||||
|
||||
var bgImageBlurring by prefDelegate(PreferKey.bgImageBlurring, 0)
|
||||
|
||||
var bgImageNBlurring by prefDelegate(PreferKey.bgImageNBlurring, 0)
|
||||
|
||||
fun hasImageBg(isDark: Boolean): Boolean {
|
||||
return if (isDark) {
|
||||
!bgImageDark.isNullOrEmpty()
|
||||
val result = if (isDark) {
|
||||
!bgImageDark.isNullOrBlank()
|
||||
} else {
|
||||
!bgImageLight.isNullOrEmpty()
|
||||
!bgImageLight.isNullOrBlank()
|
||||
}
|
||||
|
||||
Log.d(
|
||||
"MainConfig",
|
||||
"hasImageBg -> isDark=$isDark, " +
|
||||
"bgImageDark=$bgImageDark, " +
|
||||
"bgImageLight=$bgImageLight, " +
|
||||
"result=$result"
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package io.legado.app.ui.config.themeConfig
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
|
||||
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
||||
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
|
||||
import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
|
||||
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ThemeConfigScreen(
|
||||
onBackClick: () -> Unit,
|
||||
viewModel: ThemeConfigViewModel = koinViewModel()
|
||||
) {
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
var manageKey by remember { mutableStateOf<Pair<String, Boolean>?>(null) }
|
||||
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
title = { Text(stringResource(R.string.theme_setting)) },
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = {
|
||||
TopbarNavigationButton(onClick = onBackClick)
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
SplicedColumnGroup {
|
||||
SwitchSettingItem(
|
||||
title = stringResource(R.string.pure_black),
|
||||
checked = ThemeConfig.isPureBlack,
|
||||
onCheckedChange = { ThemeConfig.isPureBlack = it }
|
||||
)
|
||||
DropdownListSettingItem(
|
||||
title = stringResource(R.string.palette_style),
|
||||
selectedValue = ThemeConfig.paletteStyle,
|
||||
displayEntries = arrayOf(
|
||||
"柔和", "中性", "鲜艳", "表现力", "彩虹",
|
||||
"水果拼盘", "单色", "仿真", "内容取色"
|
||||
),
|
||||
entryValues = arrayOf(
|
||||
"tonalSpot", "neutral", "vibrant", "expressive", "rainbow",
|
||||
"fruitSalad", "monochrome", "fidelity", "content"
|
||||
),
|
||||
onValueChange = { ThemeConfig.paletteStyle = it }
|
||||
)
|
||||
}
|
||||
|
||||
SplicedColumnGroup(title = "Compose 相关") {
|
||||
SwitchSettingItem(
|
||||
title = "使用折叠应用栏",
|
||||
checked = ThemeConfig.useFlexibleTopAppBar,
|
||||
onCheckedChange = { ThemeConfig.useFlexibleTopAppBar = it }
|
||||
)
|
||||
SliderSettingItem(
|
||||
title = stringResource(R.string.container_opacity),
|
||||
description = stringResource(
|
||||
R.string.container_opacity_summary,
|
||||
ThemeConfig.containerOpacity
|
||||
),
|
||||
value = ThemeConfig.containerOpacity.toFloat(),
|
||||
defaultValue = 100f,
|
||||
valueRange = 0f..100f,
|
||||
steps = 99,
|
||||
onValueChange = { ThemeConfig.containerOpacity = it.toInt() }
|
||||
)
|
||||
SwitchSettingItem(
|
||||
title = stringResource(R.string.is_blur_enable),
|
||||
checked = ThemeConfig.enableBlur,
|
||||
onCheckedChange = { ThemeConfig.enableBlur = it }
|
||||
)
|
||||
if (ThemeConfig.enableBlur) {
|
||||
SwitchSettingItem(
|
||||
title = stringResource(R.string.is_blur_progressive_enable),
|
||||
checked = ThemeConfig.enableProgressiveBlur,
|
||||
onCheckedChange = { ThemeConfig.enableProgressiveBlur = it }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SplicedColumnGroup(title = stringResource(R.string.day)) {
|
||||
val hasLightBg = !ThemeConfig.bgImageLight.isNullOrBlank()
|
||||
ClickableSettingItem(
|
||||
title = stringResource(R.string.background_image),
|
||||
description = if (hasLightBg) stringResource(R.string.click_to_delete) else stringResource(
|
||||
R.string.select_image
|
||||
),
|
||||
onClick = { manageKey = Pair(PreferKey.bgImage, false) }
|
||||
)
|
||||
|
||||
if (hasLightBg) {
|
||||
SliderSettingItem(
|
||||
title = stringResource(R.string.background_image_blurring),
|
||||
value = ThemeConfig.bgImageBlurring.toFloat(),
|
||||
defaultValue = 0f,
|
||||
valueRange = 0f..100f,
|
||||
steps = 99,
|
||||
onValueChange = {
|
||||
ThemeConfig.bgImageBlurring = it.toInt()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SplicedColumnGroup(title = stringResource(R.string.night)) {
|
||||
val hasDarkBg = !ThemeConfig.bgImageDark.isNullOrBlank()
|
||||
ClickableSettingItem(
|
||||
title = stringResource(R.string.background_image),
|
||||
description = if (hasDarkBg) stringResource(R.string.click_to_delete) else stringResource(
|
||||
R.string.select_image
|
||||
),
|
||||
onClick = { manageKey = Pair(PreferKey.bgImageN, true) }
|
||||
)
|
||||
|
||||
if (hasDarkBg) {
|
||||
SliderSettingItem(
|
||||
title = stringResource(R.string.background_image_blurring),
|
||||
value = ThemeConfig.bgImageNBlurring.toFloat(),
|
||||
defaultValue = 0f,
|
||||
valueRange = 0f..100f,
|
||||
steps = 99,
|
||||
onValueChange = {
|
||||
ThemeConfig.bgImageNBlurring = it.toInt()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manageKey?.let { (key, isDark) ->
|
||||
BackgroundImageManageSheet(
|
||||
preferenceKey = key,
|
||||
isDarkTheme = isDark,
|
||||
onDismissRequest = { manageKey = null }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package io.legado.app.ui.config.themeConfig
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.utils.FileDoc
|
||||
import io.legado.app.utils.FileUtils
|
||||
import io.legado.app.utils.MD5Utils
|
||||
import io.legado.app.utils.externalFiles
|
||||
import io.legado.app.utils.inputStream
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import splitties.init.appCtx
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class ThemeConfigViewModel : ViewModel() {
|
||||
|
||||
/**
|
||||
* 设置背景图片
|
||||
*/
|
||||
suspend fun setBackgroundFromUri(
|
||||
uri: Uri,
|
||||
preferenceKey: String
|
||||
) = withContext(Dispatchers.IO) {
|
||||
kotlin.runCatching {
|
||||
val fileDoc = FileDoc.fromUri(uri, false)
|
||||
val suffix = fileDoc.name.substringAfterLast(".", "jpg")
|
||||
|
||||
// 计算MD5作为文件名
|
||||
val md5 = uri.inputStream(appCtx).getOrThrow().use {
|
||||
MD5Utils.md5Encode(it)
|
||||
}
|
||||
val fileName = "$md5.$suffix"
|
||||
|
||||
val folder = File(appCtx.externalFiles, preferenceKey)
|
||||
val file = File(folder, fileName)
|
||||
|
||||
if (!file.exists()) {
|
||||
FileUtils.createFileIfNotExist(file.absolutePath)
|
||||
uri.inputStream(appCtx).getOrThrow().use { input ->
|
||||
FileOutputStream(file).use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateBackgroundPath(preferenceKey, file.absolutePath)
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeBackground(preferenceKey: String) {
|
||||
updateBackgroundPath(preferenceKey, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新背景路径并清理旧文件
|
||||
*/
|
||||
private fun updateBackgroundPath(preferenceKey: String, newPath: String?) {
|
||||
val oldPath = when (preferenceKey) {
|
||||
PreferKey.bgImage -> ThemeConfig.bgImageLight
|
||||
PreferKey.bgImageN -> ThemeConfig.bgImageDark
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (oldPath != newPath && oldPath != null) {
|
||||
val oldFile = File(oldPath)
|
||||
if (oldFile.absolutePath.contains(appCtx.externalFiles.absolutePath)) {
|
||||
oldFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
when (preferenceKey) {
|
||||
PreferKey.bgImage -> ThemeConfig.bgImageLight = newPath
|
||||
PreferKey.bgImageN -> ThemeConfig.bgImageDark = newPath
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,11 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.FileOpen
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
@@ -50,11 +51,11 @@ import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheetMode
|
||||
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
|
||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
import io.legado.app.ui.widget.components.rules.RuleEditFields
|
||||
import io.legado.app.ui.widget.components.rules.RuleEditSheet
|
||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||
@@ -82,8 +83,11 @@ fun DictRuleScreen(
|
||||
var editingRule by remember { mutableStateOf<DictRule?>(null) }
|
||||
var showDeleteRuleDialog by remember { mutableStateOf<DictRule?>(null) }
|
||||
var showUrlInput by remember { mutableStateOf(false) }
|
||||
|
||||
var showFilePickerSheet by remember { mutableStateOf(false) }
|
||||
var filePickerMode by remember { mutableStateOf(FilePickerSheetMode.EXPORT) }
|
||||
var showImportSheet by remember { mutableStateOf(false) }
|
||||
var showExportSheet by remember { mutableStateOf(false) }
|
||||
|
||||
|
||||
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
||||
viewModel.moveItemInList(from.index, to.index)
|
||||
@@ -149,21 +153,38 @@ fun DictRuleScreen(
|
||||
)
|
||||
}
|
||||
|
||||
if (showFilePickerSheet) {
|
||||
if (showExportSheet) {
|
||||
FilePickerSheet(
|
||||
onDismissRequest = { showFilePickerSheet = false },
|
||||
onDismissRequest = { showExportSheet = false },
|
||||
title = stringResource(R.string.export),
|
||||
onSelectSysDir = {
|
||||
showFilePickerSheet = false
|
||||
showExportSheet = false
|
||||
exportDoc.launch("exportDictRule.json")
|
||||
},
|
||||
onUpload = {
|
||||
showFilePickerSheet = false
|
||||
showExportSheet = false
|
||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||
},
|
||||
allowExtensions = arrayOf("json")
|
||||
)
|
||||
}
|
||||
|
||||
if (showImportSheet) {
|
||||
FilePickerSheet(
|
||||
onDismissRequest = { showImportSheet = false },
|
||||
title = stringResource(R.string.import_dict_rule),
|
||||
onSelectSysFile = { types ->
|
||||
importDoc.launch(types)
|
||||
showImportSheet = false
|
||||
},
|
||||
onManualInput = {
|
||||
showUrlInput = true
|
||||
showImportSheet = false
|
||||
},
|
||||
allowExtensions = arrayOf("json", "txt")
|
||||
)
|
||||
}
|
||||
|
||||
(importState as? BaseImportUiState.Success<DictRule>)?.let { state ->
|
||||
BatchImportDialog(
|
||||
title = "导入词典规则",
|
||||
@@ -288,19 +309,11 @@ fun DictRuleScreen(
|
||||
},
|
||||
snackbarHostState = snackbarHostState,
|
||||
dropDownMenuContent = { dismiss ->
|
||||
DropdownMenuItem(
|
||||
text = { Text("在线导入") },
|
||||
onClick = { dismiss(); showUrlInput = true })
|
||||
DropdownMenuItem(
|
||||
text = { Text("本地导入") },
|
||||
onClick = {
|
||||
dismiss(); importDoc.launch(
|
||||
arrayOf(
|
||||
"text/plain",
|
||||
"application/json"
|
||||
)
|
||||
)
|
||||
})
|
||||
RoundDropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.import_str)) },
|
||||
onClick = { showImportSheet = true; dismiss() },
|
||||
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Box(
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
@@ -27,7 +26,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.NavigationBarItemDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.WideNavigationRail
|
||||
import androidx.compose.material3.WideNavigationRailItem
|
||||
@@ -54,6 +52,7 @@ import io.legado.app.ui.main.explore.ExploreScreen
|
||||
import io.legado.app.ui.main.my.MyScreen
|
||||
import io.legado.app.ui.main.rss.RssScreen
|
||||
import io.legado.app.ui.theme.regularHazeEffect
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassDefaults
|
||||
import io.legado.app.utils.startActivity
|
||||
import io.legado.app.utils.startActivityForBook
|
||||
@@ -159,20 +158,21 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.weight(1f),
|
||||
bottomBar = {
|
||||
if (!useRail && MainConfig.showBottomView) {
|
||||
val labelVisibilityMode = MainConfig.labelVisibilityMode
|
||||
val isUnlabeled = labelVisibilityMode == "unlabeled"
|
||||
NavigationBar(
|
||||
modifier = Modifier
|
||||
.regularHazeEffect(state = hazeState)
|
||||
.heightIn(max = 84.dp),
|
||||
.height(if (isUnlabeled) 64.dp else 80.dp),
|
||||
containerColor = GlassDefaults.glassColor(
|
||||
noBlurColor = BottomAppBarDefaults.containerColor,
|
||||
blurAlpha = GlassDefaults.DefaultBlurAlpha
|
||||
)
|
||||
) {
|
||||
val labelVisibilityMode = MainConfig.labelVisibilityMode
|
||||
val alwaysShowLabel = when (labelVisibilityMode) {
|
||||
"labeled" -> true
|
||||
"selected" -> false
|
||||
|
||||
@@ -24,14 +24,12 @@ import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Palette
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Source
|
||||
import androidx.compose.material.icons.filled.Translate
|
||||
import androidx.compose.material.icons.filled.Web
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -52,6 +50,7 @@ import io.legado.app.ui.config.ConfigTag
|
||||
import io.legado.app.ui.dict.rule.DictRuleActivity
|
||||
import io.legado.app.ui.file.FileManageActivity
|
||||
import io.legado.app.ui.replace.ReplaceRuleActivity
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
@@ -68,7 +67,7 @@ fun MyScreen(
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
contentWindowInsets = WindowInsets.systemBars
|
||||
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top),
|
||||
@@ -230,7 +229,7 @@ fun MyScreen(
|
||||
)
|
||||
SettingItem(
|
||||
title = stringResource(R.string.dict_rule),
|
||||
imageVector = Icons.Default.Translate,
|
||||
imageVector = Icons.AutoMirrored.Filled.Rule,
|
||||
onClick = {
|
||||
onNavigate(
|
||||
PrefClickEvent.StartActivity(DictRuleActivity::class.java)
|
||||
@@ -281,4 +280,4 @@ fun MyScreen(
|
||||
Spacer(Modifier.height(120.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.FileOpen
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -69,7 +70,6 @@ import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheetMode
|
||||
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
|
||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||
@@ -107,8 +107,9 @@ fun ReplaceRuleScreen(
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
|
||||
var showUrlInput by remember { mutableStateOf(false) }
|
||||
var showFilePickerSheet by remember { mutableStateOf(false) }
|
||||
var filePickerMode by remember { mutableStateOf(FilePickerSheetMode.EXPORT) }
|
||||
var showImportSheet by remember { mutableStateOf(false) }
|
||||
var showExportSheet by remember { mutableStateOf(false) }
|
||||
|
||||
var showDeleteRuleDialog by remember { mutableStateOf<ReplaceRule?>(null) }
|
||||
var showGroupManageSheet by remember { mutableStateOf(false) }
|
||||
|
||||
@@ -164,15 +165,31 @@ fun ReplaceRuleScreen(
|
||||
)
|
||||
}
|
||||
|
||||
if (showFilePickerSheet) {
|
||||
if (showImportSheet) {
|
||||
FilePickerSheet(
|
||||
onDismissRequest = { showFilePickerSheet = false },
|
||||
onDismissRequest = { showImportSheet = false },
|
||||
title = stringResource(R.string.import_replace_rule),
|
||||
onSelectSysFile = { types ->
|
||||
importDoc.launch(types)
|
||||
showImportSheet = false
|
||||
},
|
||||
onManualInput = {
|
||||
showUrlInput = true
|
||||
showImportSheet = false
|
||||
},
|
||||
allowExtensions = arrayOf("json", "txt")
|
||||
)
|
||||
}
|
||||
|
||||
if (showExportSheet) {
|
||||
FilePickerSheet(
|
||||
onDismissRequest = { showExportSheet = false },
|
||||
onSelectSysDir = {
|
||||
showFilePickerSheet = false
|
||||
showExportSheet = false
|
||||
exportDoc.launch("exportReplaceRule.json")
|
||||
},
|
||||
onUpload = {
|
||||
showFilePickerSheet = false
|
||||
showExportSheet = false
|
||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||
},
|
||||
allowExtensions = arrayOf("json")
|
||||
@@ -320,7 +337,7 @@ fun ReplaceRuleScreen(
|
||||
),
|
||||
ActionItem(
|
||||
text = stringResource(R.string.export),
|
||||
onClick = { showFilePickerSheet = true }
|
||||
onClick = { showExportSheet = true }
|
||||
)
|
||||
),
|
||||
onDeleteSelected = { ids ->
|
||||
@@ -389,15 +406,9 @@ fun ReplaceRuleScreen(
|
||||
snackbarHostState = snackbarHostState,
|
||||
dropDownMenuContent = { dismiss ->
|
||||
RoundDropdownMenuItem(
|
||||
text = { Text("在线导入") },
|
||||
onClick = {
|
||||
dismiss()
|
||||
showUrlInput = true // 触发输入框
|
||||
}
|
||||
)
|
||||
RoundDropdownMenuItem(
|
||||
text = { Text("本地导入") },
|
||||
onClick = { importDoc.launch(arrayOf("text/plain", "application/json")); dismiss() }
|
||||
text = { Text(stringResource(R.string.import_str)) },
|
||||
onClick = { showImportSheet = true; dismiss() },
|
||||
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||
)
|
||||
RoundDropdownMenuItem(
|
||||
text = { Text("分组管理") },
|
||||
|
||||
@@ -52,7 +52,6 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.PlainTooltip
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TooltipAnchorPosition
|
||||
@@ -78,6 +77,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.button.AlertButton
|
||||
@@ -108,7 +108,7 @@ fun ReplaceEditScreen(
|
||||
val isKeyboardVisible by keyboardAsState()
|
||||
|
||||
with(sharedTransitionScope) {
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.sharedBounds(
|
||||
@@ -487,4 +487,4 @@ fun QuickInputBar(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.legado.app.ui.theme
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
|
||||
@Composable
|
||||
fun AppBackground(
|
||||
darkTheme: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val hasImageBg = ThemeConfig.hasImageBg(darkTheme)
|
||||
val bgImagePath = if (darkTheme) ThemeConfig.bgImageDark else ThemeConfig.bgImageLight
|
||||
val blur = if (darkTheme) {
|
||||
ThemeConfig.bgImageNBlurring
|
||||
} else {
|
||||
ThemeConfig.bgImageBlurring
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
|
||||
if (hasImageBg && !bgImagePath.isNullOrBlank()) {
|
||||
AsyncImage(
|
||||
model = bgImagePath,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.blur(blur.dp),
|
||||
//TODO:低版本安卓使用coil-transformations
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
|
||||
content()
|
||||
}
|
||||
}
|
||||
@@ -20,17 +20,15 @@ fun AppTheme(
|
||||
val context = LocalContext.current
|
||||
val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
|
||||
val isPureBlack = ThemeConfig.isPureBlack
|
||||
val hasImageBg = ThemeConfig.hasImageBg(darkTheme)
|
||||
val paletteStyle = ThemeConfig.paletteStyle
|
||||
|
||||
val colorScheme =
|
||||
remember(context, appThemeMode, darkTheme, isPureBlack, hasImageBg, paletteStyle) {
|
||||
remember(context, appThemeMode, darkTheme, isPureBlack, paletteStyle) {
|
||||
ThemeManager.getColorScheme(
|
||||
context = context,
|
||||
mode = appThemeMode,
|
||||
darkTheme = darkTheme,
|
||||
isAmoled = isPureBlack,
|
||||
isImageBg = hasImageBg,
|
||||
paletteStyle = paletteStyle
|
||||
)
|
||||
}
|
||||
@@ -39,7 +37,10 @@ fun AppTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography(),
|
||||
motionScheme = MotionScheme.expressive(),
|
||||
shapes = Shapes(),
|
||||
content = content
|
||||
)
|
||||
shapes = Shapes()
|
||||
) {
|
||||
AppBackground(darkTheme = darkTheme) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ fun rememberOpaqueColorScheme(): ColorScheme {
|
||||
mode = appThemeMode,
|
||||
darkTheme = isDark,
|
||||
isAmoled = isPureBlack,
|
||||
isImageBg = hasImageBg,
|
||||
paletteStyle = paletteStyle,
|
||||
forceOpaque = true
|
||||
)
|
||||
|
||||
@@ -44,7 +44,6 @@ object ThemeManager {
|
||||
mode: AppThemeMode,
|
||||
darkTheme: Boolean,
|
||||
isAmoled: Boolean,
|
||||
isImageBg: Boolean,
|
||||
paletteStyle: String?,
|
||||
forceOpaque: Boolean = false
|
||||
): ColorScheme {
|
||||
@@ -82,7 +81,7 @@ object ThemeManager {
|
||||
)
|
||||
}
|
||||
|
||||
if (!forceOpaque && (isImageBg || actualMode == AppThemeMode.Transparent)) {
|
||||
if (!forceOpaque && actualMode == AppThemeMode.Transparent) {
|
||||
return scheme.copy(
|
||||
surface = Color.Transparent,
|
||||
background = Color.Transparent,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.legado.app.ui.widget.components
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ScaffoldDefaults
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@Composable
|
||||
fun AppScaffold(
|
||||
modifier: Modifier = Modifier,
|
||||
topBar: @Composable () -> Unit = {},
|
||||
bottomBar: @Composable () -> Unit = {},
|
||||
snackbarHost: @Composable () -> Unit = {},
|
||||
floatingActionButton: @Composable () -> Unit = {},
|
||||
floatingActionButtonPosition: FabPosition = FabPosition.End,
|
||||
containerColor: Color = Color.Transparent,
|
||||
contentColor: Color = contentColorFor(MaterialTheme.colorScheme.background),
|
||||
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
|
||||
content: @Composable (PaddingValues) -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = topBar,
|
||||
bottomBar = bottomBar,
|
||||
snackbarHost = snackbarHost,
|
||||
floatingActionButton = floatingActionButton,
|
||||
floatingActionButtonPosition = floatingActionButtonPosition,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
contentWindowInsets = contentWindowInsets,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
+6
-5
@@ -61,10 +61,9 @@ object GlassTopAppBarDefaults {
|
||||
|
||||
@Composable
|
||||
fun glassColors(): TopAppBarColors {
|
||||
val opacity = ThemeConfig.containerOpacity / 100f
|
||||
|
||||
val containerColor = GlassDefaults.glassColor(
|
||||
noBlurColor = MaterialTheme.colorScheme.surface.copy(alpha = opacity),
|
||||
noBlurColor = MaterialTheme.colorScheme.surface,
|
||||
blurAlpha = GlassDefaults.TransparentAlpha
|
||||
)
|
||||
|
||||
@@ -76,15 +75,17 @@ object GlassTopAppBarDefaults {
|
||||
|
||||
return TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = containerColor,
|
||||
scrolledContainerColor = scrolledContainerColor
|
||||
scrolledContainerColor = scrolledContainerColor,
|
||||
navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
|
||||
titleContentColor = MaterialTheme.colorScheme.onSurface,
|
||||
actionIconContentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun containerColor(): Color {
|
||||
val opacity = ThemeConfig.containerOpacity / 100f
|
||||
return GlassDefaults.glassColor(
|
||||
noBlurColor = MaterialTheme.colorScheme.surface.copy(alpha = opacity),
|
||||
noBlurColor = MaterialTheme.colorScheme.surface,
|
||||
blurAlpha = GlassDefaults.TransparentAlpha
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -24,10 +23,8 @@ fun SectionHeader(
|
||||
// 根容器的 Padding
|
||||
contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(contentPadding),
|
||||
@@ -39,5 +36,5 @@ fun SectionHeader(
|
||||
it()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.legado.app.ui.widget.components
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
@@ -11,6 +12,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
|
||||
@@ -39,10 +41,12 @@ fun SplicedColumnGroup(
|
||||
|
||||
GlassCard(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
|
||||
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.animateContentSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||
) {
|
||||
content()
|
||||
|
||||
@@ -17,32 +17,25 @@ fun GlassCard(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
shape: Shape = CardDefaults.shape,
|
||||
colors: CardColors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = ThemeConfig.containerOpacity / 100f),
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
),
|
||||
colors: CardColors? = null,
|
||||
elevation: CardElevation = CardDefaults.cardElevation(),
|
||||
border: BorderStroke? = null,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
if (onClick != null) {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
content = content
|
||||
)
|
||||
} else {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
val containerAlpha = ThemeConfig.containerOpacity / 100f
|
||||
|
||||
val finalColors = colors ?: CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = containerAlpha),
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
|
||||
Card(
|
||||
onClick = onClick ?: {},
|
||||
modifier = modifier,
|
||||
shape = shape,
|
||||
colors = finalColors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ fun FilePickerSheet(
|
||||
onSelectSysFiles?.let {
|
||||
FilePickerOptionCard(
|
||||
icon = Icons.AutoMirrored.Filled.InsertDriveFile,
|
||||
text = "系统多选",
|
||||
text = "多选项目",
|
||||
onClick = { it(typesOfExtensions(allowExtensions)) }
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,7 +40,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedToggleButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
@@ -61,6 +60,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.widget.components.AnimatedText
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -141,7 +141,7 @@ fun <T> BatchImportDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
containerColor = { it.surfaceContainer }
|
||||
) {
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.8f),
|
||||
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.PlainTooltip
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
@@ -39,6 +38,7 @@ import androidx.compose.ui.zIndex
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import io.legado.app.ui.theme.responsiveHazeEffect
|
||||
import io.legado.app.ui.theme.responsiveHazeSource
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.SelectionActions
|
||||
import io.legado.app.ui.widget.components.SelectionBottomBar
|
||||
@@ -86,9 +86,15 @@ fun <T> ListScaffold(
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
val hazeState = remember { HazeState() }
|
||||
|
||||
Scaffold(
|
||||
AppScaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
snackbarHost = {
|
||||
SnackbarHost(
|
||||
hostState = snackbarHostState,
|
||||
modifier = Modifier
|
||||
.padding(bottom = 72.dp)
|
||||
)
|
||||
},
|
||||
topBar = {
|
||||
Box(modifier = Modifier.responsiveHazeEffect(state = hazeState)) {
|
||||
DynamicTopAppBar(
|
||||
@@ -136,4 +142,4 @@ fun <T> ListScaffold(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -15,6 +15,7 @@ import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -101,6 +102,7 @@ fun CompactSliderSettingItem(
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
var sliderValue by remember(value) { mutableFloatStateOf(value) }
|
||||
var displayValue by remember(value) { mutableFloatStateOf(value) }
|
||||
|
||||
SettingItem(
|
||||
title = title,
|
||||
@@ -126,7 +128,7 @@ fun CompactSliderSettingItem(
|
||||
cornerRadius = 8.dp,
|
||||
horizontalPadding = 8.dp,
|
||||
verticalPadding = 4.dp,
|
||||
text = value.toInt().toString(),
|
||||
text = displayValue.toInt().toString(),
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
@@ -144,7 +146,10 @@ fun CompactSliderSettingItem(
|
||||
value = sliderValue,
|
||||
onValueChange = {
|
||||
sliderValue = it
|
||||
onValueChange(it)
|
||||
displayValue = it
|
||||
},
|
||||
onValueChangeFinished = {
|
||||
onValueChange(sliderValue)
|
||||
},
|
||||
valueRange = valueRange,
|
||||
steps = steps,
|
||||
@@ -152,6 +157,13 @@ fun CompactSliderSettingItem(
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(value) {
|
||||
if (!expanded) {
|
||||
sliderValue = value
|
||||
displayValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
@@ -103,7 +104,7 @@ fun SwipeActionContainer(
|
||||
content = {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
color = Color.Transparent,
|
||||
shape = RectangleShape
|
||||
) {
|
||||
content()
|
||||
|
||||
Reference in New Issue
Block a user