[修复] 修复Compose界面的背景图片显示,添加了一些界面设置选项进Compose设置页中
This commit is contained in:
38 files changed
+721
-158
No files matched your search
@@ -3,15 +3,14 @@ package io.legado.app.base
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import io.legado.app.constant.EventBus
|
import io.legado.app.constant.EventBus
|
||||||
import io.legado.app.constant.Theme
|
import io.legado.app.constant.Theme
|
||||||
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.config.OldThemeConfig
|
import io.legado.app.help.config.OldThemeConfig
|
||||||
import io.legado.app.ui.theme.AppTheme
|
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.disableAutoFill
|
||||||
import io.legado.app.utils.fullScreen
|
import io.legado.app.utils.fullScreen
|
||||||
import io.legado.app.utils.observeEvent
|
import io.legado.app.utils.observeEvent
|
||||||
@@ -40,11 +39,9 @@ abstract class BaseComposeActivity(
|
|||||||
// Compose 入口
|
// Compose 入口
|
||||||
setContent {
|
setContent {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
Surface() {
|
|
||||||
Content()
|
Content()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (imageBg) {
|
if (imageBg) {
|
||||||
upBackgroundImage()
|
upBackgroundImage()
|
||||||
|
|||||||
@@ -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.coverConfig.CoverConfigViewModel
|
||||||
import io.legado.app.ui.config.otherConfig.OtherConfigViewModel
|
import io.legado.app.ui.config.otherConfig.OtherConfigViewModel
|
||||||
import io.legado.app.ui.config.readConfig.ReadConfigViewModel
|
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.dict.rule.DictRuleViewModel
|
||||||
import io.legado.app.ui.main.MainViewModel
|
import io.legado.app.ui.main.MainViewModel
|
||||||
import io.legado.app.ui.main.bookshelf.BookshelfViewModel
|
import io.legado.app.ui.main.bookshelf.BookshelfViewModel
|
||||||
@@ -92,6 +93,7 @@ val appModule = module {
|
|||||||
viewModelOf(::OtherConfigViewModel)
|
viewModelOf(::OtherConfigViewModel)
|
||||||
viewModelOf(::ReadConfigViewModel)
|
viewModelOf(::ReadConfigViewModel)
|
||||||
viewModelOf(::CoverConfigViewModel)
|
viewModelOf(::CoverConfigViewModel)
|
||||||
|
viewModelOf(::ThemeConfigViewModel)
|
||||||
viewModelOf(::TocViewModel)
|
viewModelOf(::TocViewModel)
|
||||||
viewModelOf(::RemoteBookViewModel)
|
viewModelOf(::RemoteBookViewModel)
|
||||||
viewModelOf(::BookInfoViewModel)
|
viewModelOf(::BookInfoViewModel)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import androidx.compose.material3.FilledTonalIconButton
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -32,6 +31,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.AppConst.appInfo
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
@@ -55,7 +55,7 @@ fun AboutScreen(
|
|||||||
|
|
||||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
GlassMediumFlexibleTopAppBar(
|
GlassMediumFlexibleTopAppBar(
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -40,6 +39,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import io.legado.app.data.entities.Bookmark
|
import io.legado.app.data.entities.Bookmark
|
||||||
import io.legado.app.ui.widget.CollapsibleHeader
|
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.EmptyMessageView
|
||||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
@@ -93,7 +93,7 @@ fun AllBookmarkScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import androidx.compose.material3.FilterChip
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
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.responsiveHazeEffect
|
||||||
import io.legado.app.ui.theme.responsiveHazeSource
|
import io.legado.app.ui.theme.responsiveHazeSource
|
||||||
import io.legado.app.ui.widget.components.AnimatedTextLine
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SearchBarSection
|
import io.legado.app.ui.widget.components.SearchBarSection
|
||||||
@@ -286,7 +286,7 @@ fun ExploreShowScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -47,6 +46,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.ui.book.changecover.ChangeCoverDialog
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.button.TopBarButtonVariant
|
import io.legado.app.ui.widget.components.button.TopBarButtonVariant
|
||||||
@@ -67,7 +67,7 @@ fun BookInfoEditScreen(
|
|||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
GlassMediumFlexibleTopAppBar(
|
GlassMediumFlexibleTopAppBar(
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Surface
|
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.ReadRecord
|
||||||
import io.legado.app.data.entities.readRecord.ReadRecordDetail
|
import io.legado.app.data.entities.readRecord.ReadRecordDetail
|
||||||
import io.legado.app.ui.widget.components.AnimatedTextLine
|
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.EmptyMessageView
|
||||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
@@ -142,7 +142,7 @@ fun ReadRecordScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||||
topBar = {
|
topBar = {
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import androidx.compose.material3.ListItem
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.PlainTooltip
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TooltipAnchorPosition
|
import androidx.compose.material3.TooltipAnchorPosition
|
||||||
import androidx.compose.material3.TooltipBox
|
import androidx.compose.material3.TooltipBox
|
||||||
@@ -66,6 +65,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.data.entities.SearchContentHistory
|
import io.legado.app.data.entities.SearchContentHistory
|
||||||
import io.legado.app.ui.widget.components.AnimatedText
|
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.EmptyMessageView
|
||||||
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
@@ -135,7 +135,7 @@ fun SearchContentScreen(
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PrimaryScrollableTabRow
|
import androidx.compose.material3.PrimaryScrollableTabRow
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.Text
|
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.replace.ReplaceEditRoute
|
||||||
import io.legado.app.ui.widget.CollapsibleHeader
|
import io.legado.app.ui.widget.CollapsibleHeader
|
||||||
import io.legado.app.ui.widget.components.ActionItem
|
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.EmptyMessageView
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SelectionBottomBar
|
import io.legado.app.ui.widget.components.SelectionBottomBar
|
||||||
@@ -295,7 +295,7 @@ fun TocScreen(
|
|||||||
viewModel.clearSelection()
|
viewModel.clearSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
DynamicTopAppBar(
|
DynamicTopAppBar(
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.FileOpen
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.SnackbarHostState
|
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.button.SmallIconButton
|
||||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
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.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.BaseImportUiState
|
||||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
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.importComponents.SourceInputDialog
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
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.RuleEditFields
|
||||||
import io.legado.app.ui.widget.components.rules.RuleEditSheet
|
import io.legado.app.ui.widget.components.rules.RuleEditSheet
|
||||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||||
@@ -84,9 +85,10 @@ fun TxtRuleScreen(
|
|||||||
var showEditSheet by remember { mutableStateOf(false) }
|
var showEditSheet by remember { mutableStateOf(false) }
|
||||||
var editingRule by remember { mutableStateOf<TxtTocRule?>(null) }
|
var editingRule by remember { mutableStateOf<TxtTocRule?>(null) }
|
||||||
var showDeleteRuleDialog by remember { mutableStateOf<TxtTocRule?>(null) }
|
var showDeleteRuleDialog by remember { mutableStateOf<TxtTocRule?>(null) }
|
||||||
|
|
||||||
var showUrlInput by remember { mutableStateOf(false) }
|
var showUrlInput by remember { mutableStateOf(false) }
|
||||||
var showFilePickerSheet by remember { mutableStateOf(false) }
|
var showImportSheet by remember { mutableStateOf(false) }
|
||||||
var filePickerMode by remember { mutableStateOf(FilePickerSheetMode.EXPORT) }
|
var showExportSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
||||||
viewModel.moveItemInList(from.index, to.index)
|
viewModel.moveItemInList(from.index, to.index)
|
||||||
@@ -152,21 +154,38 @@ fun TxtRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showFilePickerSheet) {
|
if (showExportSheet) {
|
||||||
FilePickerSheet(
|
FilePickerSheet(
|
||||||
onDismissRequest = { showFilePickerSheet = false },
|
onDismissRequest = { showExportSheet = false },
|
||||||
|
title = stringResource(R.string.export),
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showFilePickerSheet = false
|
showExportSheet = false
|
||||||
exportDoc.launch("exportDictRule.json")
|
exportDoc.launch("exportDictRule.json")
|
||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showFilePickerSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json")
|
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 ->
|
(importState as? BaseImportUiState.Success<TxtTocRule>)?.let { state ->
|
||||||
BatchImportDialog(
|
BatchImportDialog(
|
||||||
title = "导入词典规则",
|
title = "导入词典规则",
|
||||||
@@ -279,7 +298,7 @@ fun TxtRuleScreen(
|
|||||||
}),
|
}),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = stringResource(R.string.export),
|
text = stringResource(R.string.export),
|
||||||
onClick = { showFilePickerSheet = true })
|
onClick = { showExportSheet = true })
|
||||||
),
|
),
|
||||||
onDeleteSelected = { ids ->
|
onDeleteSelected = { ids ->
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -292,19 +311,11 @@ fun TxtRuleScreen(
|
|||||||
},
|
},
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
DropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { Text("在线导入") },
|
text = { Text(stringResource(R.string.import_str)) },
|
||||||
onClick = { dismiss(); showUrlInput = true })
|
onClick = { showImportSheet = true; dismiss() },
|
||||||
DropdownMenuItem(
|
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||||
text = { Text("本地导入") },
|
|
||||||
onClick = {
|
|
||||||
dismiss(); importDoc.launch(
|
|
||||||
arrayOf(
|
|
||||||
"text/plain",
|
|
||||||
"application/json"
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -53,11 +53,16 @@ fun <T> prefDelegate(
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun readInitialValue(): T {
|
private fun readInitialValue(): T {
|
||||||
return when (defaultValue) {
|
val prefs = appCtx.defaultSharedPreferences
|
||||||
is String -> appCtx.getPrefString(key, defaultValue) as T
|
return when {
|
||||||
is Int -> appCtx.getPrefInt(key, defaultValue) as T
|
defaultValue is String -> appCtx.getPrefString(key, defaultValue) as T
|
||||||
is Boolean -> appCtx.getPrefBoolean(key, defaultValue) as T
|
defaultValue == null && prefs.contains(key) -> {
|
||||||
is Long -> appCtx.getPrefLong(key, defaultValue) as T
|
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
|
else -> defaultValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
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.coverConfig.CoverConfigScreen
|
||||||
import io.legado.app.ui.config.otherConfig.OtherConfigScreen
|
import io.legado.app.ui.config.otherConfig.OtherConfigScreen
|
||||||
import io.legado.app.ui.config.readConfig.ReadConfigScreen
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
|
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.ClickableSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
class TestConfigActivity : BaseComposeActivity() {
|
class TestConfigActivity : BaseComposeActivity() {
|
||||||
@@ -44,6 +43,9 @@ class TestConfigActivity : BaseComposeActivity() {
|
|||||||
@Serializable
|
@Serializable
|
||||||
object CoverConfigRoute
|
object CoverConfigRoute
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object ThemeConfigRoute
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
@@ -57,7 +59,8 @@ class TestConfigActivity : BaseComposeActivity() {
|
|||||||
onBackClick = { finish() },
|
onBackClick = { finish() },
|
||||||
onNavigateToOther = { navController.navigate(OtherConfigRoute) },
|
onNavigateToOther = { navController.navigate(OtherConfigRoute) },
|
||||||
onNavigateToRead = { navController.navigate(ReadConfigRoute) },
|
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> {
|
composable<CoverConfigRoute> {
|
||||||
CoverConfigScreen(onBackClick = { navController.popBackStack() })
|
CoverConfigScreen(onBackClick = { navController.popBackStack() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable<ThemeConfigRoute> {
|
||||||
|
ThemeConfigScreen(onBackClick = { navController.popBackStack() })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,11 +88,12 @@ class TestConfigActivity : BaseComposeActivity() {
|
|||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
onNavigateToOther: () -> Unit,
|
onNavigateToOther: () -> Unit,
|
||||||
onNavigateToRead: () -> Unit,
|
onNavigateToRead: () -> Unit,
|
||||||
onNavigateToCover: () -> Unit
|
onNavigateToCover: () -> Unit,
|
||||||
|
onNavigateToTheme: () -> Unit
|
||||||
) {
|
) {
|
||||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
GlassMediumFlexibleTopAppBar(
|
GlassMediumFlexibleTopAppBar(
|
||||||
@@ -105,10 +113,9 @@ class TestConfigActivity : BaseComposeActivity() {
|
|||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
SplicedColumnGroup {
|
SplicedColumnGroup {
|
||||||
SwitchSettingItem(
|
ClickableSettingItem(
|
||||||
title = "使用折叠应用栏",
|
title = stringResource(R.string.theme_setting),
|
||||||
checked = ThemeConfig.useFlexibleTopAppBar,
|
onClick = onNavigateToTheme
|
||||||
onCheckedChange = { ThemeConfig.useFlexibleTopAppBar = it }
|
|
||||||
)
|
)
|
||||||
ClickableSettingItem(
|
ClickableSettingItem(
|
||||||
title = stringResource(R.string.other_setting),
|
title = stringResource(R.string.other_setting),
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -27,6 +26,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.PreferKey
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
@@ -48,7 +48,7 @@ fun CoverConfigScreen(
|
|||||||
var manageKey by remember { mutableStateOf<String?>(null) }
|
var manageKey by remember { mutableStateOf<String?>(null) }
|
||||||
var showColorPickerByField by remember { mutableStateOf<String?>(null) }
|
var showColorPickerByField by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
GlassMediumFlexibleTopAppBar(
|
GlassMediumFlexibleTopAppBar(
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
@@ -30,6 +29,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.service.WebService
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
@@ -79,7 +79,7 @@ fun OtherConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
GlassMediumFlexibleTopAppBar(
|
GlassMediumFlexibleTopAppBar(
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
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.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
@@ -41,7 +41,7 @@ fun ReadConfigScreen(
|
|||||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
var showPageKeySheet by remember { mutableStateOf(false) }
|
var showPageKeySheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
GlassMediumFlexibleTopAppBar(
|
GlassMediumFlexibleTopAppBar(
|
||||||
|
|||||||
@@ -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
|
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.constant.PreferKey
|
||||||
import io.legado.app.ui.config.prefDelegate
|
import io.legado.app.ui.config.prefDelegate
|
||||||
|
import io.legado.app.utils.postEvent
|
||||||
|
|
||||||
object ThemeConfig {
|
object ThemeConfig {
|
||||||
|
|
||||||
@@ -19,16 +22,34 @@ object ThemeConfig {
|
|||||||
|
|
||||||
var isPureBlack by prefDelegate(PreferKey.pureBlack, false)
|
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 {
|
fun hasImageBg(isDark: Boolean): Boolean {
|
||||||
return if (isDark) {
|
val result = if (isDark) {
|
||||||
!bgImageDark.isNullOrEmpty()
|
!bgImageDark.isNullOrBlank()
|
||||||
} else {
|
} 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.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.FileOpen
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.SnackbarHostState
|
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.button.SmallIconButton
|
||||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
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.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.BaseImportUiState
|
||||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
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.importComponents.SourceInputDialog
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
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.RuleEditFields
|
||||||
import io.legado.app.ui.widget.components.rules.RuleEditSheet
|
import io.legado.app.ui.widget.components.rules.RuleEditSheet
|
||||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||||
@@ -82,8 +83,11 @@ fun DictRuleScreen(
|
|||||||
var editingRule by remember { mutableStateOf<DictRule?>(null) }
|
var editingRule by remember { mutableStateOf<DictRule?>(null) }
|
||||||
var showDeleteRuleDialog by remember { mutableStateOf<DictRule?>(null) }
|
var showDeleteRuleDialog by remember { mutableStateOf<DictRule?>(null) }
|
||||||
var showUrlInput by remember { mutableStateOf(false) }
|
var showUrlInput by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var showFilePickerSheet 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 ->
|
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
||||||
viewModel.moveItemInList(from.index, to.index)
|
viewModel.moveItemInList(from.index, to.index)
|
||||||
@@ -149,21 +153,38 @@ fun DictRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showFilePickerSheet) {
|
if (showExportSheet) {
|
||||||
FilePickerSheet(
|
FilePickerSheet(
|
||||||
onDismissRequest = { showFilePickerSheet = false },
|
onDismissRequest = { showExportSheet = false },
|
||||||
|
title = stringResource(R.string.export),
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showFilePickerSheet = false
|
showExportSheet = false
|
||||||
exportDoc.launch("exportDictRule.json")
|
exportDoc.launch("exportDictRule.json")
|
||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showFilePickerSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json")
|
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 ->
|
(importState as? BaseImportUiState.Success<DictRule>)?.let { state ->
|
||||||
BatchImportDialog(
|
BatchImportDialog(
|
||||||
title = "导入词典规则",
|
title = "导入词典规则",
|
||||||
@@ -288,19 +309,11 @@ fun DictRuleScreen(
|
|||||||
},
|
},
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
DropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { Text("在线导入") },
|
text = { Text(stringResource(R.string.import_str)) },
|
||||||
onClick = { dismiss(); showUrlInput = true })
|
onClick = { showImportSheet = true; dismiss() },
|
||||||
DropdownMenuItem(
|
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||||
text = { Text("本地导入") },
|
|
||||||
onClick = {
|
|
||||||
dismiss(); importDoc.launch(
|
|
||||||
arrayOf(
|
|
||||||
"text/plain",
|
|
||||||
"application/json"
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
@@ -27,7 +26,6 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
import androidx.compose.material3.NavigationBarItem
|
import androidx.compose.material3.NavigationBarItem
|
||||||
import androidx.compose.material3.NavigationBarItemDefaults
|
import androidx.compose.material3.NavigationBarItemDefaults
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.WideNavigationRail
|
import androidx.compose.material3.WideNavigationRail
|
||||||
import androidx.compose.material3.WideNavigationRailItem
|
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.my.MyScreen
|
||||||
import io.legado.app.ui.main.rss.RssScreen
|
import io.legado.app.ui.main.rss.RssScreen
|
||||||
import io.legado.app.ui.theme.regularHazeEffect
|
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.ui.widget.components.GlassDefaults
|
||||||
import io.legado.app.utils.startActivity
|
import io.legado.app.utils.startActivity
|
||||||
import io.legado.app.utils.startActivityForBook
|
import io.legado.app.utils.startActivityForBook
|
||||||
@@ -159,20 +158,21 @@ fun MainScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
if (!useRail && MainConfig.showBottomView) {
|
if (!useRail && MainConfig.showBottomView) {
|
||||||
|
val labelVisibilityMode = MainConfig.labelVisibilityMode
|
||||||
|
val isUnlabeled = labelVisibilityMode == "unlabeled"
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.regularHazeEffect(state = hazeState)
|
.regularHazeEffect(state = hazeState)
|
||||||
.heightIn(max = 84.dp),
|
.height(if (isUnlabeled) 64.dp else 80.dp),
|
||||||
containerColor = GlassDefaults.glassColor(
|
containerColor = GlassDefaults.glassColor(
|
||||||
noBlurColor = BottomAppBarDefaults.containerColor,
|
noBlurColor = BottomAppBarDefaults.containerColor,
|
||||||
blurAlpha = GlassDefaults.DefaultBlurAlpha
|
blurAlpha = GlassDefaults.DefaultBlurAlpha
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
val labelVisibilityMode = MainConfig.labelVisibilityMode
|
|
||||||
val alwaysShowLabel = when (labelVisibilityMode) {
|
val alwaysShowLabel = when (labelVisibilityMode) {
|
||||||
"labeled" -> true
|
"labeled" -> true
|
||||||
"selected" -> false
|
"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.Palette
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material.icons.filled.Source
|
import androidx.compose.material.icons.filled.Source
|
||||||
import androidx.compose.material.icons.filled.Translate
|
|
||||||
import androidx.compose.material.icons.filled.Web
|
import androidx.compose.material.icons.filled.Web
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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.dict.rule.DictRuleActivity
|
||||||
import io.legado.app.ui.file.FileManageActivity
|
import io.legado.app.ui.file.FileManageActivity
|
||||||
import io.legado.app.ui.replace.ReplaceRuleActivity
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||||
@@ -68,7 +67,7 @@ fun MyScreen(
|
|||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
contentWindowInsets = WindowInsets.systemBars
|
contentWindowInsets = WindowInsets.systemBars
|
||||||
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top),
|
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top),
|
||||||
@@ -230,7 +229,7 @@ fun MyScreen(
|
|||||||
)
|
)
|
||||||
SettingItem(
|
SettingItem(
|
||||||
title = stringResource(R.string.dict_rule),
|
title = stringResource(R.string.dict_rule),
|
||||||
imageVector = Icons.Default.Translate,
|
imageVector = Icons.AutoMirrored.Filled.Rule,
|
||||||
onClick = {
|
onClick = {
|
||||||
onNavigate(
|
onNavigate(
|
||||||
PrefClickEvent.StartActivity(DictRuleActivity::class.java)
|
PrefClickEvent.StartActivity(DictRuleActivity::class.java)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.FileOpen
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
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.card.ReorderableSelectionItem
|
||||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
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.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.BaseImportUiState
|
||||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
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.importComponents.SourceInputDialog
|
||||||
@@ -107,8 +107,9 @@ fun ReplaceRuleScreen(
|
|||||||
val hapticFeedback = LocalHapticFeedback.current
|
val hapticFeedback = LocalHapticFeedback.current
|
||||||
|
|
||||||
var showUrlInput by remember { mutableStateOf(false) }
|
var showUrlInput by remember { mutableStateOf(false) }
|
||||||
var showFilePickerSheet by remember { mutableStateOf(false) }
|
var showImportSheet by remember { mutableStateOf(false) }
|
||||||
var filePickerMode by remember { mutableStateOf(FilePickerSheetMode.EXPORT) }
|
var showExportSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var showDeleteRuleDialog by remember { mutableStateOf<ReplaceRule?>(null) }
|
var showDeleteRuleDialog by remember { mutableStateOf<ReplaceRule?>(null) }
|
||||||
var showGroupManageSheet by remember { mutableStateOf(false) }
|
var showGroupManageSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -164,15 +165,31 @@ fun ReplaceRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showFilePickerSheet) {
|
if (showImportSheet) {
|
||||||
FilePickerSheet(
|
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 = {
|
onSelectSysDir = {
|
||||||
showFilePickerSheet = false
|
showExportSheet = false
|
||||||
exportDoc.launch("exportReplaceRule.json")
|
exportDoc.launch("exportReplaceRule.json")
|
||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showFilePickerSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json")
|
allowExtensions = arrayOf("json")
|
||||||
@@ -320,7 +337,7 @@ fun ReplaceRuleScreen(
|
|||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = stringResource(R.string.export),
|
text = stringResource(R.string.export),
|
||||||
onClick = { showFilePickerSheet = true }
|
onClick = { showExportSheet = true }
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
onDeleteSelected = { ids ->
|
onDeleteSelected = { ids ->
|
||||||
@@ -389,15 +406,9 @@ fun ReplaceRuleScreen(
|
|||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { Text("在线导入") },
|
text = { Text(stringResource(R.string.import_str)) },
|
||||||
onClick = {
|
onClick = { showImportSheet = true; dismiss() },
|
||||||
dismiss()
|
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||||
showUrlInput = true // 触发输入框
|
|
||||||
}
|
|
||||||
)
|
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = { Text("本地导入") },
|
|
||||||
onClick = { importDoc.launch(arrayOf("text/plain", "application/json")); dismiss() }
|
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { Text("分组管理") },
|
text = { Text("分组管理") },
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.PlainTooltip
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TooltipAnchorPosition
|
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.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.button.AlertButton
|
import io.legado.app.ui.widget.components.button.AlertButton
|
||||||
@@ -108,7 +108,7 @@ fun ReplaceEditScreen(
|
|||||||
val isKeyboardVisible by keyboardAsState()
|
val isKeyboardVisible by keyboardAsState()
|
||||||
|
|
||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.sharedBounds(
|
.sharedBounds(
|
||||||
|
|||||||
@@ -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 context = LocalContext.current
|
||||||
val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
|
val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
|
||||||
val isPureBlack = ThemeConfig.isPureBlack
|
val isPureBlack = ThemeConfig.isPureBlack
|
||||||
val hasImageBg = ThemeConfig.hasImageBg(darkTheme)
|
|
||||||
val paletteStyle = ThemeConfig.paletteStyle
|
val paletteStyle = ThemeConfig.paletteStyle
|
||||||
|
|
||||||
val colorScheme =
|
val colorScheme =
|
||||||
remember(context, appThemeMode, darkTheme, isPureBlack, hasImageBg, paletteStyle) {
|
remember(context, appThemeMode, darkTheme, isPureBlack, paletteStyle) {
|
||||||
ThemeManager.getColorScheme(
|
ThemeManager.getColorScheme(
|
||||||
context = context,
|
context = context,
|
||||||
mode = appThemeMode,
|
mode = appThemeMode,
|
||||||
darkTheme = darkTheme,
|
darkTheme = darkTheme,
|
||||||
isAmoled = isPureBlack,
|
isAmoled = isPureBlack,
|
||||||
isImageBg = hasImageBg,
|
|
||||||
paletteStyle = paletteStyle
|
paletteStyle = paletteStyle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -39,7 +37,10 @@ fun AppTheme(
|
|||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
typography = Typography(),
|
typography = Typography(),
|
||||||
motionScheme = MotionScheme.expressive(),
|
motionScheme = MotionScheme.expressive(),
|
||||||
shapes = Shapes(),
|
shapes = Shapes()
|
||||||
content = content
|
) {
|
||||||
)
|
AppBackground(darkTheme = darkTheme) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,6 @@ fun rememberOpaqueColorScheme(): ColorScheme {
|
|||||||
mode = appThemeMode,
|
mode = appThemeMode,
|
||||||
darkTheme = isDark,
|
darkTheme = isDark,
|
||||||
isAmoled = isPureBlack,
|
isAmoled = isPureBlack,
|
||||||
isImageBg = hasImageBg,
|
|
||||||
paletteStyle = paletteStyle,
|
paletteStyle = paletteStyle,
|
||||||
forceOpaque = true
|
forceOpaque = true
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ object ThemeManager {
|
|||||||
mode: AppThemeMode,
|
mode: AppThemeMode,
|
||||||
darkTheme: Boolean,
|
darkTheme: Boolean,
|
||||||
isAmoled: Boolean,
|
isAmoled: Boolean,
|
||||||
isImageBg: Boolean,
|
|
||||||
paletteStyle: String?,
|
paletteStyle: String?,
|
||||||
forceOpaque: Boolean = false
|
forceOpaque: Boolean = false
|
||||||
): ColorScheme {
|
): ColorScheme {
|
||||||
@@ -82,7 +81,7 @@ object ThemeManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!forceOpaque && (isImageBg || actualMode == AppThemeMode.Transparent)) {
|
if (!forceOpaque && actualMode == AppThemeMode.Transparent) {
|
||||||
return scheme.copy(
|
return scheme.copy(
|
||||||
surface = Color.Transparent,
|
surface = Color.Transparent,
|
||||||
background = 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
|
@Composable
|
||||||
fun glassColors(): TopAppBarColors {
|
fun glassColors(): TopAppBarColors {
|
||||||
val opacity = ThemeConfig.containerOpacity / 100f
|
|
||||||
|
|
||||||
val containerColor = GlassDefaults.glassColor(
|
val containerColor = GlassDefaults.glassColor(
|
||||||
noBlurColor = MaterialTheme.colorScheme.surface.copy(alpha = opacity),
|
noBlurColor = MaterialTheme.colorScheme.surface,
|
||||||
blurAlpha = GlassDefaults.TransparentAlpha
|
blurAlpha = GlassDefaults.TransparentAlpha
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -76,15 +75,17 @@ object GlassTopAppBarDefaults {
|
|||||||
|
|
||||||
return TopAppBarDefaults.topAppBarColors(
|
return TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = containerColor,
|
containerColor = containerColor,
|
||||||
scrolledContainerColor = scrolledContainerColor
|
scrolledContainerColor = scrolledContainerColor,
|
||||||
|
navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
titleContentColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
actionIconContentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun containerColor(): Color {
|
fun containerColor(): Color {
|
||||||
val opacity = ThemeConfig.containerOpacity / 100f
|
|
||||||
return GlassDefaults.glassColor(
|
return GlassDefaults.glassColor(
|
||||||
noBlurColor = MaterialTheme.colorScheme.surface.copy(alpha = opacity),
|
noBlurColor = MaterialTheme.colorScheme.surface,
|
||||||
blurAlpha = GlassDefaults.TransparentAlpha
|
blurAlpha = GlassDefaults.TransparentAlpha
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -24,9 +23,7 @@ fun SectionHeader(
|
|||||||
// 根容器的 Padding
|
// 根容器的 Padding
|
||||||
contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
||||||
) {
|
) {
|
||||||
Surface(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -39,5 +36,5 @@ fun SectionHeader(
|
|||||||
it()
|
it()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
@@ -11,6 +12,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.ui.widget.components.card.GlassCard
|
import io.legado.app.ui.widget.components.card.GlassCard
|
||||||
|
|
||||||
@@ -39,10 +41,12 @@ fun SplicedColumnGroup(
|
|||||||
|
|
||||||
GlassCard(
|
GlassCard(
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
|
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.animateContentSize(),
|
||||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
|
|||||||
@@ -17,32 +17,25 @@ fun GlassCard(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
shape: Shape = CardDefaults.shape,
|
shape: Shape = CardDefaults.shape,
|
||||||
colors: CardColors = CardDefaults.cardColors(
|
colors: CardColors? = null,
|
||||||
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = ThemeConfig.containerOpacity / 100f),
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
|
||||||
),
|
|
||||||
elevation: CardElevation = CardDefaults.cardElevation(),
|
elevation: CardElevation = CardDefaults.cardElevation(),
|
||||||
border: BorderStroke? = null,
|
border: BorderStroke? = null,
|
||||||
content: @Composable ColumnScope.() -> Unit
|
content: @Composable ColumnScope.() -> Unit
|
||||||
) {
|
) {
|
||||||
if (onClick != null) {
|
val containerAlpha = ThemeConfig.containerOpacity / 100f
|
||||||
|
|
||||||
|
val finalColors = colors ?: CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = containerAlpha),
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
)
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
onClick = onClick,
|
onClick = onClick ?: {},
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
shape = shape,
|
shape = shape,
|
||||||
colors = colors,
|
colors = finalColors,
|
||||||
elevation = elevation,
|
elevation = elevation,
|
||||||
border = border,
|
border = border,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
Card(
|
|
||||||
modifier = modifier,
|
|
||||||
shape = shape,
|
|
||||||
colors = colors,
|
|
||||||
elevation = elevation,
|
|
||||||
border = border,
|
|
||||||
content = content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ fun FilePickerSheet(
|
|||||||
onSelectSysFiles?.let {
|
onSelectSysFiles?.let {
|
||||||
FilePickerOptionCard(
|
FilePickerOptionCard(
|
||||||
icon = Icons.AutoMirrored.Filled.InsertDriveFile,
|
icon = Icons.AutoMirrored.Filled.InsertDriveFile,
|
||||||
text = "系统多选",
|
text = "多选项目",
|
||||||
onClick = { it(typesOfExtensions(allowExtensions)) }
|
onClick = { it(typesOfExtensions(allowExtensions)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -40,7 +40,6 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.OutlinedToggleButton
|
import androidx.compose.material3.OutlinedToggleButton
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -61,6 +60,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.ui.widget.components.AnimatedText
|
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 io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ fun <T> BatchImportDialog(
|
|||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
containerColor = { it.surfaceContainer }
|
containerColor = { it.surfaceContainer }
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.8f),
|
.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.FloatingToolbarDefaults.ScreenOffset
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.PlainTooltip
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -39,6 +38,7 @@ import androidx.compose.ui.zIndex
|
|||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import io.legado.app.ui.theme.responsiveHazeEffect
|
import io.legado.app.ui.theme.responsiveHazeEffect
|
||||||
import io.legado.app.ui.theme.responsiveHazeSource
|
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.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.widget.components.SelectionActions
|
import io.legado.app.ui.widget.components.SelectionActions
|
||||||
import io.legado.app.ui.widget.components.SelectionBottomBar
|
import io.legado.app.ui.widget.components.SelectionBottomBar
|
||||||
@@ -86,9 +86,15 @@ fun <T> ListScaffold(
|
|||||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
val hazeState = remember { HazeState() }
|
val hazeState = remember { HazeState() }
|
||||||
|
|
||||||
Scaffold(
|
AppScaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
snackbarHost = {
|
||||||
|
SnackbarHost(
|
||||||
|
hostState = snackbarHostState,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = 72.dp)
|
||||||
|
)
|
||||||
|
},
|
||||||
topBar = {
|
topBar = {
|
||||||
Box(modifier = Modifier.responsiveHazeEffect(state = hazeState)) {
|
Box(modifier = Modifier.responsiveHazeEffect(state = hazeState)) {
|
||||||
DynamicTopAppBar(
|
DynamicTopAppBar(
|
||||||
|
|||||||
+14
-2
@@ -15,6 +15,7 @@ import androidx.compose.material3.Slider
|
|||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableFloatStateOf
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -101,6 +102,7 @@ fun CompactSliderSettingItem(
|
|||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var sliderValue by remember(value) { mutableFloatStateOf(value) }
|
var sliderValue by remember(value) { mutableFloatStateOf(value) }
|
||||||
|
var displayValue by remember(value) { mutableFloatStateOf(value) }
|
||||||
|
|
||||||
SettingItem(
|
SettingItem(
|
||||||
title = title,
|
title = title,
|
||||||
@@ -126,7 +128,7 @@ fun CompactSliderSettingItem(
|
|||||||
cornerRadius = 8.dp,
|
cornerRadius = 8.dp,
|
||||||
horizontalPadding = 8.dp,
|
horizontalPadding = 8.dp,
|
||||||
verticalPadding = 4.dp,
|
verticalPadding = 4.dp,
|
||||||
text = value.toInt().toString(),
|
text = displayValue.toInt().toString(),
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
|
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
contentColor = MaterialTheme.colorScheme.onSurface
|
contentColor = MaterialTheme.colorScheme.onSurface
|
||||||
)
|
)
|
||||||
@@ -144,7 +146,10 @@ fun CompactSliderSettingItem(
|
|||||||
value = sliderValue,
|
value = sliderValue,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
sliderValue = it
|
sliderValue = it
|
||||||
onValueChange(it)
|
displayValue = it
|
||||||
|
},
|
||||||
|
onValueChangeFinished = {
|
||||||
|
onValueChange(sliderValue)
|
||||||
},
|
},
|
||||||
valueRange = valueRange,
|
valueRange = valueRange,
|
||||||
steps = steps,
|
steps = steps,
|
||||||
@@ -152,6 +157,13 @@ fun CompactSliderSettingItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
LaunchedEffect(value) {
|
||||||
|
if (!expanded) {
|
||||||
|
sliderValue = value
|
||||||
|
displayValue = value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
@@ -103,7 +104,7 @@ fun SwipeActionContainer(
|
|||||||
content = {
|
content = {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
color = MaterialTheme.colorScheme.surface,
|
color = Color.Transparent,
|
||||||
shape = RectangleShape
|
shape = RectangleShape
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
|
|||||||
Reference in new issue
Block a user