[优化] 一些主题优化

This commit is contained in:
HapeLee
2026-02-26 01:31:02 +08:00
parent fb020356b6
commit 0250001a27
45 changed files with 361 additions and 327 deletions
+3 -4
View File
@@ -10,7 +10,6 @@ import android.content.res.Configuration
import android.graphics.BitmapFactory
import android.os.Build
import androidx.core.graphics.scale
import coil.ImageLoader
import com.github.liuyueyi.quick.transfer.constants.TransType
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.DynamicColorsOptions
@@ -44,9 +43,9 @@ import io.legado.app.help.LifecycleHelp
import io.legado.app.help.RuleBigDataHelp
import io.legado.app.help.book.BookHelp
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.OldThemeConfig.applyDayNightInit
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.ThemeConfig.applyDayNightInit
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.http.Cronet
import io.legado.app.help.http.ObsoleteUrlFactory
@@ -155,7 +154,7 @@ class App : Application() {
BookHelp.clearInvalidCache()
Backup.clearCache()
ReadBookConfig.clearBgAndCache()
ThemeConfig.clearBg()
OldThemeConfig.clearBg()
//初始化简繁转换引擎
when (AppConfig.chineseConverterType) {
1 -> {
@@ -27,9 +27,8 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.constant.Theme
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.theme.ThemeSyncer
import io.legado.app.utils.applyOpenTint
import io.legado.app.utils.applyTint
import io.legado.app.utils.disableAutoFill
@@ -232,7 +231,7 @@ abstract class BaseActivity<VB : ViewBinding>(
open fun upBackgroundImage() {
if (imageBg) {
try {
ThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
OldThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
window.decorView.background = it.toDrawable(resources)
}
} catch (e: OutOfMemoryError) {
@@ -245,7 +244,6 @@ abstract class BaseActivity<VB : ViewBinding>(
open fun observeLiveBus() {
observeEvent<String>(EventBus.RECREATE) {
ThemeSyncer.syncAll()
recreate()
}
}
@@ -9,9 +9,8 @@ import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.WindowCompat
import io.legado.app.constant.EventBus
import io.legado.app.constant.Theme
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.ui.theme.AppTheme
import io.legado.app.ui.theme.ThemeSyncer
import io.legado.app.utils.disableAutoFill
import io.legado.app.utils.fullScreen
import io.legado.app.utils.observeEvent
@@ -66,7 +65,7 @@ abstract class BaseComposeActivity(
open fun upBackgroundImage() {
try {
ThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
OldThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
window.setBackgroundDrawable(it.toDrawable(resources))
}
} catch (_: Exception) {}
@@ -74,7 +73,6 @@ abstract class BaseComposeActivity(
open fun observeLiveBus() {
observeEvent<String>(EventBus.RECREATE) {
ThemeSyncer.syncAll()
recreate()
}
}
@@ -181,6 +181,7 @@ object PreferKey {
const val composeEngine = "composeEngine"
const val containerOpacity = "containerOpacity"
const val enableBlur = "enableBlur"
const val enableProgressiveBlur = "enableProgressiveBlur"
const val cBackground = "colorBackground"
const val cBBackground = "colorBottomBackground"
const val bgImage = "backgroundImage"
@@ -8,8 +8,8 @@ import io.legado.app.data.entities.KeyboardAssist
import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.BookCover
import io.legado.app.utils.GSON
@@ -70,12 +70,12 @@ object DefaultData {
GSON.fromJsonArray<TxtTocRule>(json).getOrNull() ?: emptyList()
}
val themeConfigs: List<ThemeConfig.Config> by lazy {
val themeConfigs: List<OldThemeConfig.Config> by lazy {
val json = String(
appCtx.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}")
appCtx.assets.open("defaultData${File.separator}${OldThemeConfig.configFileName}")
.readBytes()
)
GSON.fromJsonArray<ThemeConfig.Config>(json).getOrNull() ?: emptyList()
GSON.fromJsonArray<OldThemeConfig.Config>(json).getOrNull() ?: emptyList()
}
val rssSources: List<RssSource> by lazy {
@@ -15,8 +15,8 @@ import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.BaseSource
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.http.BackstageWebView
import io.legado.app.help.http.CookieManager.cookieJarHeader
import io.legado.app.help.http.CookieStore
@@ -1203,12 +1203,12 @@ interface JsExtensions : JsEncodeUtils {
*/
@JavascriptInterface
fun getThemeConfig(): String {
val themeConfig = ThemeConfig.getDurConfig(appCtx)
val themeConfig = OldThemeConfig.getDurConfig(appCtx)
return GSON.toJson(themeConfig)
}
fun getThemeConfigMap(): Map<String, Any?> {
return ThemeConfig.getDurConfig(appCtx).toMap()
return OldThemeConfig.getDurConfig(appCtx).toMap()
}
}
@@ -32,7 +32,7 @@ import splitties.init.appCtx
import java.io.File
@Keep
object ThemeConfig {
object OldThemeConfig {
const val configFileName = "themeConfig.json"
val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
@@ -13,8 +13,8 @@ import io.legado.app.help.AppWebDav
import io.legado.app.help.DirectLinkUpload
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.BookCover
import io.legado.app.utils.FileUtils
@@ -82,7 +82,7 @@ object Backup {
DirectLinkUpload.ruleFileName,
ReadBookConfig.configFileName,
ReadBookConfig.shareConfigFileName,
ThemeConfig.configFileName,
OldThemeConfig.configFileName,
BookCover.configFileName,
"config.xml"
)
@@ -169,8 +169,8 @@ object Backup {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName)
.writeText(it)
}
GSON.toJson(ThemeConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName)
GSON.toJson(OldThemeConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + OldThemeConfig.configFileName)
.writeText(it)
}
DirectLinkUpload.getConfig()?.let {
@@ -33,8 +33,8 @@ import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.book.isLocal
import io.legado.app.help.book.upType
import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.model.BookCover
import io.legado.app.model.localBook.LocalBook
import io.legado.app.utils.ACache
@@ -216,12 +216,12 @@ object Restore {
AppLog.put("恢复直链上传出错\n${it.localizedMessage}", it)
}
//恢复主题配置
File(path, ThemeConfig.configFileName).takeIf {
File(path, OldThemeConfig.configFileName).takeIf {
it.exists()
}?.runCatching {
FileUtils.delete(ThemeConfig.configFilePath)
copyTo(File(ThemeConfig.configFilePath))
ThemeConfig.upConfig()
FileUtils.delete(OldThemeConfig.configFilePath)
copyTo(File(OldThemeConfig.configFilePath))
OldThemeConfig.upConfig()
}?.onFailure {
AppLog.put("恢复主题出错\n${it.localizedMessage}", it)
}
@@ -301,7 +301,7 @@ object Restore {
if (!BuildConfig.DEBUG) {
LauncherIconHelp.changeIcon(appCtx.getPrefString(PreferKey.launcherIcon))
}
ThemeConfig.applyDayNight(appCtx)
OldThemeConfig.applyDayNight(appCtx)
}
}
@@ -21,7 +21,7 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.theme.ThemeSyncer
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.utils.getPrefString
import io.legado.app.utils.postEvent
import io.legado.app.utils.restart
@@ -91,13 +91,13 @@ class ThemeCardPreference(context: Context, attrs: AttributeSet) : Preference(co
return@setOnClickListener
} else {
AppConfig.containerOpacity = 0
ThemeSyncer.syncContainerOpacity()
}
}
val oldValue = currentValue
currentValue = value
persistString(value)
callChangeListener(value)
ThemeConfig.appTheme = value
notifyDataSetChanged()
val isDynamicSwitch = (oldValue == "12" || value == "12")
Handler(Looper.getMainLooper()).postDelayed({
@@ -7,7 +7,7 @@ import android.util.AttributeSet
import androidx.preference.PreferenceViewHolder
import com.google.android.material.button.MaterialButtonToggleGroup
import io.legado.app.R
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.OldThemeConfig
class ThemeModePreference(context: Context, attrs: AttributeSet) : Preference(context, attrs) {
@@ -59,7 +59,7 @@ class ThemeModePreference(context: Context, attrs: AttributeSet) : Preference(co
persistString(newValue)
callChangeListener(newValue)
Handler(Looper.getMainLooper()).postDelayed({
ThemeConfig.applyDayNight(context)
OldThemeConfig.applyDayNight(context)
}, 300)
}
}
@@ -35,7 +35,7 @@ import io.legado.app.R
import io.legado.app.constant.AppConst.appInfo
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SplicedColumnGroup
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.settingItem.SettingItem
@@ -61,7 +61,7 @@ fun AboutScreen(
GlassMediumFlexibleTopAppBar(
title = { Text(stringResource(R.string.about)) },
navigationIcon = {
SmallTopBarButton(onClick = onBack)
TopbarNavigationButton(onClick = onBack)
},
scrollBehavior = scrollBehavior
)
@@ -14,7 +14,7 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemSourceImportBinding
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.OldThemeConfig
//import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.dialog.CodeDialog
import io.legado.app.ui.widget.dialog.WaitDialog
@@ -126,7 +126,7 @@ class ImportThemeDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_recycl
}
inner class SourcesAdapter(context: Context) :
RecyclerAdapter<ThemeConfig.Config, ItemSourceImportBinding>(context) {
RecyclerAdapter<OldThemeConfig.Config, ItemSourceImportBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemSourceImportBinding {
return ItemSourceImportBinding.inflate(inflater, parent, false)
@@ -135,7 +135,7 @@ class ImportThemeDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_recycl
override fun convert(
holder: ItemViewHolder,
binding: ItemSourceImportBinding,
item: ThemeConfig.Config,
item: OldThemeConfig.Config,
payloads: MutableList<Any>
) {
binding.apply {
@@ -8,12 +8,19 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.http.decompressed
import io.legado.app.help.http.newCallResponseBody
import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text
import io.legado.app.utils.*
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.isJsonArray
import io.legado.app.utils.isJsonObject
import io.legado.app.utils.isUri
import io.legado.app.utils.readText
import splitties.init.appCtx
class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
@@ -21,8 +28,8 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<ThemeConfig.Config>()
val checkSources = arrayListOf<ThemeConfig.Config?>()
val allSources = arrayListOf<OldThemeConfig.Config>()
val checkSources = arrayListOf<OldThemeConfig.Config?>()
val selectStatus = arrayListOf<Boolean>()
val isSelectAll: Boolean
@@ -50,7 +57,7 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
execute {
selectStatus.forEachIndexed { index, b ->
if (b) {
ThemeConfig.addConfig(allSources[index])
OldThemeConfig.addConfig(allSources[index])
}
}
}.onFinally {
@@ -72,11 +79,12 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
private suspend fun importSourceAwait(text: String) {
when {
text.isJsonObject() -> {
GSON.fromJsonObject<ThemeConfig.Config>(text).getOrThrow().let {
GSON.fromJsonObject<OldThemeConfig.Config>(text).getOrThrow().let {
allSources.add(it)
}
}
text.isJsonArray() -> GSON.fromJsonArray<ThemeConfig.Config>(text).getOrThrow()
text.isJsonArray() -> GSON.fromJsonArray<OldThemeConfig.Config>(text).getOrThrow()
.let { items ->
allSources.addAll(items)
}
@@ -106,7 +114,7 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() {
execute {
allSources.forEach { config ->
val source = ThemeConfig.configList.find {
val source = OldThemeConfig.configList.find {
it.themeName == config.themeName
}
checkSources.add(source)
@@ -22,8 +22,6 @@ import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.UnfoldLess
import androidx.compose.material.icons.filled.UnfoldMore
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
@@ -48,9 +46,11 @@ import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet
import io.legado.app.ui.widget.components.bookmark.BookmarkItem
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.lazylist.Scroller
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
@@ -105,7 +105,7 @@ fun AllBookmarkScreen(
},
scrollBehavior = scrollBehavior,
navigationIcon = {
SmallTopBarButton(onClick = onBack)
TopbarNavigationButton(onClick = onBack)
},
actions = {
if (bookmarksGrouped.isNotEmpty()) {
@@ -127,11 +127,11 @@ fun AllBookmarkScreen(
IconButton(onClick = { showMenu = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu")
}
DropdownMenu(
RoundDropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false }
) {
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("导出 JSON") },
onClick = {
showMenu = false
@@ -139,7 +139,7 @@ fun AllBookmarkScreen(
exportLauncher.launch(null)
}
)
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("导出 Markdown") },
onClick = {
showMenu = false
@@ -53,6 +53,7 @@ class ExploreShowViewModel(
private val _rawBooks = MutableStateFlow<List<SearchBook>>(emptyList())
private val _filterState = MutableStateFlow(BookFilterState.fromId(AppConfig.exploreFilterState))
private val _isLoading = MutableStateFlow(false)
private val _isRefreshing = MutableStateFlow(false)
private val _errorMsg = MutableStateFlow<String?>(null)
private var bookSource: BookSource? = null
private var exploreUrl: String? = null
@@ -68,10 +69,14 @@ class ExploreShowViewModel(
private val _gridCount = MutableStateFlow(appCtx.exploreLayoutGrid)
val gridCount = _gridCount.asStateFlow()
val isEnd: StateFlow<Boolean> = _isEndStateFlow.asStateFlow()
val isRefreshing = _isRefreshing.asStateFlow()
fun saveGridCount(value: Int) {
appCtx.exploreLayoutGrid = value
_gridCount.value = value
}
val uiBooks = combine(
_rawBooks,
_filterState,
@@ -50,8 +50,8 @@ import io.legado.app.R
import io.legado.app.ui.book.changecover.ChangeCoverDialog
import io.legado.app.ui.widget.components.Cover
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopBarButtonVariant
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.utils.SelectImageContract
import io.legado.app.utils.launch
import io.legado.app.utils.showDialogFragment
@@ -72,13 +72,13 @@ fun BookInfoEditScreen(
GlassMediumFlexibleTopAppBar(
title = { Text(text = stringResource(id = R.string.book_info_edit)) },
navigationIcon = {
SmallTopBarButton(
TopbarNavigationButton(
onClick = onBack,
style = TopBarButtonVariant.Outlined
)
},
actions = {
SmallTopBarButton(
TopbarNavigationButton(
onClick = { viewModel.save(onSave) },
imageVector = Icons.Default.Save
)
@@ -33,8 +33,8 @@ import io.legado.app.constant.PreferKey
import io.legado.app.databinding.ViewReadMenuBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.source.getSourceType
import io.legado.app.lib.dialogs.alert
@@ -693,7 +693,7 @@ class ReadMenu @JvmOverloads constructor(
description = context.getString(R.string.day_night_switch),
onClick = {
AppConfig.isNightTheme = !AppConfig.isNightTheme
ThemeConfig.applyDayNight(context)
OldThemeConfig.applyDayNight(context)
buttonMap["theme"]?.setIconResource(
if (AppConfig.isNightTheme) R.drawable.ic_daytime else R.drawable.ic_brightness
)
@@ -15,8 +15,8 @@ import io.legado.app.constant.EventBus
import io.legado.app.databinding.DialogReadBookStyleBinding
import io.legado.app.databinding.ItemReadStyleBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.model.ReadBook
import io.legado.app.ui.book.read.ReadBookActivity
@@ -121,7 +121,7 @@ class ReadStyleDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_book_
}
tvDayNight.setOnClickListener {
AppConfig.isNightTheme = !AppConfig.isNightTheme
ThemeConfig.applyDayNight(requireContext())
OldThemeConfig.applyDayNight(requireContext())
}
// rgPageAnim.setOnCheckedChangeListener { _, checkedId ->
// ReadBook.book?.setPageAnim(-1)
@@ -47,6 +47,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -87,7 +88,7 @@ import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.SectionHeader
import io.legado.app.ui.widget.components.button.AlertButton
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarTopBar
import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
import io.legado.app.ui.widget.components.heatmap.HeatmapLegend
@@ -98,6 +99,7 @@ import io.legado.app.ui.widget.components.heatmap.WeekdayLabelsColumn
import io.legado.app.ui.widget.components.heatmap.rememberDateRange
import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange
import io.legado.app.ui.widget.components.heatmap.rememberWeeks
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
import io.legado.app.ui.widget.components.swipe.SwipeAction
import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
import io.legado.app.utils.StringUtils.formatFriendlyDate
@@ -163,7 +165,7 @@ fun ReadRecordScreen(
)
},
navigationIcon = {
SmallTopBarButton(onClick = onBackClick)
TopbarNavigationButton(onClick = onBackClick)
},
actions = {
IconButton(onClick = {
@@ -318,8 +320,8 @@ fun ReadRecordScreen(
if (showCalendar) {
val sheetState =
androidx.compose.material3.rememberModalBottomSheetState(skipPartiallyExpanded = true)
androidx.compose.material3.ModalBottomSheet(
rememberModalBottomSheetState(skipPartiallyExpanded = true)
GlassModalBottomSheet(
onDismissRequest = { showCalendar = false },
sheetState = sheetState
) {
@@ -58,7 +58,7 @@ import io.legado.app.ui.widget.components.EmptyMessageView
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.button.AnimatedActionButton
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import org.koin.androidx.compose.koinViewModel
@@ -141,7 +141,7 @@ fun SearchContentScreen(
)
},
navigationIcon = {
SmallTopBarButton(onClick = onBack)
TopbarNavigationButton(onClick = onBack)
},
actions = {
Row(
@@ -294,33 +294,33 @@ fun TocScreen(
when (pagerState.currentPage) {
0 -> {
RoundDropdownMenuItem(
text = "使用替换规则",
text = { Text("使用替换规则") },
trailingIcon = {
Checkbox(checked = useReplace, onCheckedChange = null)
},
onClick = { viewModel.toggleUseReplace() }
)
RoundDropdownMenuItem(
text = "显示字数",
text = { Text("显示字数") },
trailingIcon = {
Checkbox(checked = showWordCount, onCheckedChange = null)
},
onClick = { viewModel.toggleShowWordCount() }
)
RoundDropdownMenuItem(
text = "反转目录",
text = { Text("反转目录") },
onClick = { viewModel.reverseToc() }
)
PillDivider()
RoundDropdownMenuItem(
text = "替换规则",
text = { Text("替换规则") },
onClick = {
onOpenReplaceRule(null)
dismiss()
}
)
RoundDropdownMenuItem(
text = "新建替换规则",
text = { Text("新建替换规则") },
onClick = {
val scopes = mutableListOf<String>()
book?.name?.let { scopes.add(it) }
@@ -340,7 +340,7 @@ fun TocScreen(
if (book?.isLocal == true) {
PillHeaderDivider(title = "本地书籍选项")
RoundDropdownMenuItem(
text = "本地书籍目录规则",
text = { Text("本地书籍目录规则") },
onClick = {
val intent =
Intent(context, TxtTocRuleActivity::class.java).apply {
@@ -351,7 +351,7 @@ fun TocScreen(
}
)
RoundDropdownMenuItem(
text = "拆分超长章节",
text = { Text("拆分超长章节") },
trailingIcon = {
Checkbox(
checked = viewModel.isSplitLongChapter,
@@ -368,7 +368,7 @@ fun TocScreen(
else -> {
RoundDropdownMenuItem(
text = "导出书签为JSON",
text = { Text("导出书签为JSON") },
onClick = {
val dateFormat = SimpleDateFormat(
"yyyyMMdd_HHmm",
@@ -380,7 +380,7 @@ fun TocScreen(
}
)
RoundDropdownMenuItem(
text = "导出书签为MarkDown",
text = { Text("导出书签为MarkDown") },
onClick = {
val dateFormat = SimpleDateFormat(
"yyyyMMdd_HHmm",
@@ -445,13 +445,13 @@ fun TocScreen(
onDismissRequest = { showVolumeMenu = false }
) {
RoundDropdownMenuItem(
text = "展开所有卷",
text = { Text("展开所有卷") },
onClick = {
viewModel.expandAllVolumes(); showVolumeMenu = false
}
)
RoundDropdownMenuItem(
text = "收起所有卷",
text = { Text("收起所有卷") },
onClick = {
viewModel.collapseAllVolumes(); showVolumeMenu = false
}
@@ -463,7 +463,7 @@ fun TocScreen(
PillHeaderDivider(title = "快速跳转")
volumeItems.forEach { uiItem ->
RoundDropdownMenuItem(
text = uiItem.title,
text = { Text(uiItem.title) },
onClick = {
scope.launch {
val targetIndex =
@@ -22,7 +22,7 @@ import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.databinding.DialogImageBlurringBinding
import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.prefs.ColorPreference
@@ -32,7 +32,7 @@ import io.legado.app.lib.prefs.ThemeCardPreference
import io.legado.app.lib.prefs.ThemeModePreference
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.theme.ThemeSyncer
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
import io.legado.app.utils.ColorUtils
@@ -181,13 +181,20 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
}
PreferKey.paletteStyle, PreferKey.pureBlack, PreferKey.enableBlur, PreferKey.swipeAnimation -> {
ThemeSyncer.syncAll()
PreferKey.paletteStyle, PreferKey.pureBlack, PreferKey.swipeAnimation -> {
Handler(Looper.getMainLooper()).postDelayed({
recreateActivities()
}, 100)
}
PreferKey.enableBlur -> {
ThemeConfig.enableBlur = PreferKey.enableBlur.toBoolean()
}
PreferKey.enableProgressiveBlur -> {
ThemeConfig.enableProgressiveBlur = PreferKey.enableProgressiveBlur.toBoolean()
}
PreferKey.customMode -> handleRestartRequired()
PreferKey.isPredictiveBackEnabled -> {
@@ -263,17 +270,15 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
.setTitle(getString(R.string.container_opacity))
.setMaxValue(100)
.setMinValue(0)
.setValue(AppConfig.containerOpacity)
.setValue(ThemeConfig.containerOpacity)
.setCustomButton((R.string.btn_default_s)) {
AppConfig.containerOpacity = 100
ThemeSyncer.syncContainerOpacity()
ThemeConfig.containerOpacity = 100
Handler(Looper.getMainLooper()).postDelayed({
upPreferenceSummary(PreferKey.containerOpacity)
}, 100)
}
.show {
AppConfig.containerOpacity = it
ThemeSyncer.syncContainerOpacity()
ThemeConfig.containerOpacity = it
Handler(Looper.getMainLooper()).postDelayed({
upPreferenceSummary(PreferKey.containerOpacity)
}, 100)
@@ -305,11 +310,11 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
alertBinding.editView.text?.toString()?.let { themeName ->
when (key) {
"saveDayTheme" -> {
ThemeConfig.saveDayTheme(requireContext(), themeName)
OldThemeConfig.saveDayTheme(requireContext(), themeName)
}
"saveNightTheme" -> {
ThemeConfig.saveNightTheme(requireContext(), themeName)
OldThemeConfig.saveNightTheme(requireContext(), themeName)
}
}
}
@@ -2,7 +2,6 @@ package io.legado.app.ui.config
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
@@ -15,11 +14,10 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemThemeConfigBinding
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.primaryColor
//import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding
@@ -54,14 +52,14 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
}
fun initData() {
adapter.setItems(ThemeConfig.configList)
adapter.setItems(OldThemeConfig.configList)
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_import -> {
requireContext().getClipText()?.let {
if (ThemeConfig.addConfig(it)) {
if (OldThemeConfig.addConfig(it)) {
initData()
} else {
toastOnUi("格式不对,添加失败")
@@ -75,7 +73,7 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
fun delete(index: Int) {
alert(R.string.delete, R.string.sure_del) {
yesButton {
ThemeConfig.delConfig(index)
OldThemeConfig.delConfig(index)
initData()
}
noButton()
@@ -83,12 +81,12 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
}
fun share(index: Int) {
val json = GSON.toJson(ThemeConfig.configList[index])
val json = GSON.toJson(OldThemeConfig.configList[index])
requireContext().share(json, "主题分享")
}
inner class Adapter(context: Context) :
RecyclerAdapter<ThemeConfig.Config, ItemThemeConfigBinding>(context) {
RecyclerAdapter<OldThemeConfig.Config, ItemThemeConfigBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemThemeConfigBinding {
return ItemThemeConfigBinding.inflate(inflater, parent, false)
@@ -97,7 +95,7 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
override fun convert(
holder: ItemViewHolder,
binding: ItemThemeConfigBinding,
item: ThemeConfig.Config,
item: OldThemeConfig.Config,
payloads: MutableList<Any>
) {
binding.apply {
@@ -113,7 +111,10 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
override fun registerListener(holder: ItemViewHolder, binding: ItemThemeConfigBinding) {
binding.apply {
root.setOnClickListener {
ThemeConfig.applyConfig(context, ThemeConfig.configList[holder.layoutPosition])
OldThemeConfig.applyConfig(
context,
OldThemeConfig.configList[holder.layoutPosition]
)
}
ivShare.setOnClickListener {
share(holder.layoutPosition)
@@ -34,7 +34,7 @@ import io.legado.app.R
import io.legado.app.service.WebService
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SplicedColumnGroup
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.exportComponents.FilePickerSheet
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
@@ -89,7 +89,7 @@ fun OtherConfigScreen(
},
scrollBehavior = scrollBehavior,
navigationIcon = {
SmallTopBarButton(onClick = onBackClick)
TopbarNavigationButton(onClick = onBackClick)
}
)
}
@@ -0,0 +1,31 @@
package io.legado.app.ui.config.themeConfig
import io.legado.app.constant.PreferKey
import io.legado.app.ui.config.prefDelegate
object ThemeConfig {
var containerOpacity by prefDelegate(PreferKey.containerOpacity, 100)
var enableBlur by prefDelegate(PreferKey.enableBlur, false)
var enableProgressiveBlur by prefDelegate(PreferKey.enableProgressiveBlur, true)
var paletteStyle by prefDelegate(PreferKey.paletteStyle, "tonalSpot")
var appTheme by prefDelegate(PreferKey.appTheme, "0")
var isPureBlack by prefDelegate(PreferKey.pureBlack, false)
var bgImageLight by prefDelegate<String?>(PreferKey.bgImage, null)
var bgImageDark by prefDelegate<String?>(PreferKey.bgImageN, null)
fun hasImageBg(isDark: Boolean): Boolean {
return if (isDark) {
!bgImageDark.isNullOrEmpty()
} else {
!bgImageLight.isNullOrEmpty()
}
}
}
@@ -73,6 +73,7 @@ import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.rules.RuleListScaffold
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
@@ -387,35 +388,35 @@ fun ReplaceRuleScreen(
},
snackbarHostState = snackbarHostState,
dropDownMenuContent = { dismiss ->
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("在线导入") },
onClick = {
dismiss()
showUrlInput = true // 触发输入框
}
)
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("本地导入") },
onClick = { importDoc.launch(arrayOf("text/plain", "application/json")); dismiss() }
)
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("分组管理") },
onClick = { showGroupManageSheet = true; dismiss() }
)
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("帮助") },
onClick = { /*TODO*/ dismiss() }
)
HorizontalDivider()
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("旧的在前") },
onClick = { viewModel.setSortMode("asc"); dismiss() }
)
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("新的在前") },
onClick = { viewModel.setSortMode("desc"); dismiss() }
)
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("名称升序") },
onClick = {
viewModel.setSortMode("name_asc")
@@ -425,7 +426,7 @@ fun ReplaceRuleScreen(
}
}
)
DropdownMenuItem(
RoundDropdownMenuItem(
text = { Text("名称降序") },
onClick = {
viewModel.setSortMode("name_desc")
@@ -81,7 +81,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.button.AlertButton
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import org.koin.androidx.compose.koinViewModel
@Composable
@@ -121,7 +121,7 @@ fun ReplaceEditScreen(
GlassMediumFlexibleTopAppBar(
title = { Text(if (state.id > 0) "编辑替换规则" else "新增替换规则") },
navigationIcon = {
SmallTopBarButton(onClick = onBack)
TopbarNavigationButton(onClick = onBack)
},
actions = {
AnimatedVisibility(
@@ -7,8 +7,9 @@ import androidx.compose.material3.MotionScheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import io.legado.app.ui.config.themeConfig.ThemeConfig
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@@ -16,18 +17,23 @@ fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val appThemeMode by ThemeState.themeMode.collectAsState()
val isPureBlack by ThemeState.isPureBlack.collectAsState()
val hasImageBg by ThemeState.hasImageBg.collectAsState()
val paletteStyle by ThemeState.paletteStyle.collectAsState()
val context = LocalContext.current
val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
val isPureBlack = ThemeConfig.isPureBlack
val hasImageBg = ThemeConfig.hasImageBg(darkTheme)
val paletteStyle = ThemeConfig.paletteStyle
val colorScheme = ThemeManager.getColorScheme(
mode = appThemeMode,
darkTheme = darkTheme,
isAmoled = isPureBlack,
isImageBg = hasImageBg,
paletteStyle = paletteStyle
)
val colorScheme =
remember(context, appThemeMode, darkTheme, isPureBlack, hasImageBg, paletteStyle) {
ThemeManager.getColorScheme(
context = context,
mode = appThemeMode,
darkTheme = darkTheme,
isAmoled = isPureBlack,
isImageBg = hasImageBg,
paletteStyle = paletteStyle
)
}
MaterialExpressiveTheme(
colorScheme = colorScheme,
@@ -36,5 +42,4 @@ fun AppTheme(
shapes = Shapes(),
content = content
)
}
@@ -1,15 +1,11 @@
package io.legado.app.ui.theme
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
abstract class BaseColorScheme {
abstract val lightScheme: ColorScheme
abstract val darkScheme: ColorScheme
@Composable
@ReadOnlyComposable
fun getColorScheme(darkTheme: Boolean): ColorScheme {
return if (darkTheme) darkScheme else lightScheme
}
@@ -0,0 +1,31 @@
package io.legado.app.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.theme.ThemeManager.getColorScheme
@Composable
fun rememberOpaqueColorScheme(): ColorScheme {
val context = LocalContext.current
val isDark = isSystemInDarkTheme()
val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
val isPureBlack = ThemeConfig.isPureBlack
val hasImageBg = ThemeConfig.hasImageBg(isDark)
val paletteStyle = ThemeConfig.paletteStyle
return remember(context, appThemeMode, isDark, isPureBlack, hasImageBg, paletteStyle) {
getColorScheme(
context = context,
mode = appThemeMode,
darkTheme = isDark,
isAmoled = isPureBlack,
isImageBg = hasImageBg,
paletteStyle = paletteStyle,
forceOpaque = true
)
}
}
@@ -1,13 +1,11 @@
package io.legado.app.ui.theme
import android.content.Context
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.theme.ThemeResolver.resolvePaletteStyle
import io.legado.app.ui.theme.colorScheme.AugustColorScheme
@@ -40,16 +38,17 @@ object ThemeManager {
AppThemeMode.Transparent to TransparentColorScheme,
)
@Composable
fun getColorScheme(
context: Context,
mode: AppThemeMode,
darkTheme: Boolean = isSystemInDarkTheme(),
darkTheme: Boolean,
isAmoled: Boolean,
isImageBg: Boolean,
paletteStyle: String?,
forceOpaque: Boolean = false
): ColorScheme {
val context = LocalContext.current
val style = resolvePaletteStyle(paletteStyle)
val actualMode = if (forceOpaque && mode == AppThemeMode.Transparent) {
AppThemeMode.WH
@@ -1,73 +0,0 @@
package io.legado.app.ui.theme
import io.legado.app.constant.PreferKey
import io.legado.app.help.config.AppConfig
import io.legado.app.utils.getPrefBoolean
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import splitties.init.appCtx
object ThemeState {
private val _themeMode =
MutableStateFlow(ThemeResolver.resolveThemeMode(AppConfig.AppTheme.toString()))
val themeMode: StateFlow<AppThemeMode> = _themeMode.asStateFlow()
private val _isPureBlack =
MutableStateFlow(appCtx.getPrefBoolean(PreferKey.pureBlack, false))
val isPureBlack: StateFlow<Boolean> = _isPureBlack.asStateFlow()
private val _hasImageBg =
MutableStateFlow(AppConfig.hasImageBg)
val hasImageBg: StateFlow<Boolean> = _hasImageBg.asStateFlow()
private val _paletteStyle =
MutableStateFlow(AppConfig.paletteStyle)
val paletteStyle: StateFlow<String?> = _paletteStyle.asStateFlow()
private val _enableBlur =
MutableStateFlow(AppConfig.enableBlur)
val enableBlur: StateFlow<Boolean> = _enableBlur.asStateFlow()
private val _containerOpacity =
MutableStateFlow(AppConfig.containerOpacity)
val containerOpacity: StateFlow<Int> = _containerOpacity
fun updateThemeMode(newMode: AppThemeMode) {
if (_themeMode.value != newMode) {
_themeMode.value = newMode
}
}
fun updatePureBlack(enabled: Boolean) {
if (_isPureBlack.value != enabled) {
_isPureBlack.value = enabled
}
}
fun updateImageBg(enabled: Boolean) {
if (_hasImageBg.value != enabled) {
_hasImageBg.value = enabled
}
}
fun updatePaletteStyle(style: String?) {
if (_paletteStyle.value != style) {
_paletteStyle.value = style
}
}
fun updateEnableBlur(enabled: Boolean) {
if (_enableBlur.value != enabled) {
_enableBlur.value = enabled
}
}
fun updateContainerOpacity(opacity: Int) {
if (_containerOpacity.value != opacity) {
_containerOpacity.value = opacity
}
}
}
@@ -1,45 +0,0 @@
package io.legado.app.ui.theme
import io.legado.app.constant.PreferKey
import io.legado.app.help.config.AppConfig
import io.legado.app.utils.getPrefBoolean
import splitties.init.appCtx
object ThemeSyncer {
fun syncAll() {
syncThemeMode()
syncPureBlack()
syncImageBg()
syncPaletteStyle()
syncEnableBlur()
syncContainerOpacity()
}
fun syncThemeMode() {
ThemeState.updateThemeMode(
ThemeResolver.resolveThemeMode(AppConfig.AppTheme.toString())
)
}
fun syncPureBlack() {
ThemeState.updatePureBlack(appCtx.getPrefBoolean(PreferKey.pureBlack, false))
}
fun syncImageBg() {
ThemeState.updateImageBg(AppConfig.hasImageBg)
}
fun syncPaletteStyle() {
ThemeState.updatePaletteStyle(AppConfig.paletteStyle)
}
fun syncEnableBlur() {
ThemeState.updateEnableBlur(AppConfig.enableBlur)
}
fun syncContainerOpacity() {
ThemeState.updateContainerOpacity(AppConfig.containerOpacity)
}
}
@@ -5,14 +5,13 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MediumFlexibleTopAppBar
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import io.legado.app.ui.theme.ThemeState
import io.legado.app.ui.config.themeConfig.ThemeConfig
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
@@ -22,30 +21,9 @@ fun GlassMediumFlexibleTopAppBar(
subtitle: (@Composable () -> Unit)? = null,
scrollBehavior: TopAppBarScrollBehavior? = null,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {}
actions: @Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = GlassTopAppBarDefaults.glassColors()
) {
val opacityValue by ThemeState.containerOpacity.collectAsState()
val enableBlur by ThemeState.enableBlur.collectAsState()
val alpha = opacityValue / 100f
val containerColor = if (enableBlur) {
Color.Transparent
} else {
MaterialTheme.colorScheme.surface.copy(alpha = alpha)
}
val scrolledContainerColor = if (enableBlur) {
Color.Transparent
} else {
MaterialTheme.colorScheme.surfaceContainer.copy(alpha = alpha)
}
val appBarColors = TopAppBarDefaults.topAppBarColors(
containerColor = containerColor,
scrolledContainerColor = scrolledContainerColor
)
MediumFlexibleTopAppBar(
modifier = modifier,
title = title,
@@ -53,7 +31,46 @@ fun GlassMediumFlexibleTopAppBar(
navigationIcon = navigationIcon,
actions = actions,
scrollBehavior = scrollBehavior,
colors = appBarColors
colors = colors
)
}
object GlassTopAppBarDefaults {
@Composable
fun glassColors(): TopAppBarColors {
val alpha = ThemeConfig.containerOpacity / 100f
val enableBlur = ThemeConfig.enableBlur
val containerColor = if (enableBlur) {
MaterialTheme.colorScheme.surface
} else {
MaterialTheme.colorScheme.surface.copy(alpha = alpha)
}
val scrolledContainerColor = if (enableBlur) {
MaterialTheme.colorScheme.surface.copy(alpha = 0f)
} else {
MaterialTheme.colorScheme.surfaceContainer
}
return TopAppBarDefaults.topAppBarColors(
containerColor = containerColor,
scrolledContainerColor = scrolledContainerColor
)
}
@Composable
fun controlContainerColor(): Color {
val enableBlur = ThemeConfig.enableBlur
val baseColor = MaterialTheme.colorScheme.surfaceContainerHigh
return if (enableBlur) {
val alpha = 0.6f
baseColor.copy(alpha = alpha)
} else {
baseColor.copy(alpha = ThemeConfig.containerOpacity / 100f)
}
}
}
@@ -17,31 +17,26 @@ import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedIconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.ThemeState
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
enum class TopBarButtonVariant {
Filled, Outlined, Icon
}
@Composable
fun SmallTopBarButton(
fun TopBarButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription: String? = null,
style: TopBarButtonVariant = TopBarButtonVariant.Filled
) {
val themeOpacity by ThemeState.containerOpacity.collectAsState()
val baseColor = MaterialTheme.colorScheme.surfaceContainerHigh
val glassColor = baseColor.copy(alpha = (themeOpacity / 100f).coerceAtLeast(0.6f))
val commonModifier = modifier
.padding(horizontal = 12.dp)
.size(36.dp)
when (style) {
@@ -50,7 +45,7 @@ fun SmallTopBarButton(
onClick = onClick,
modifier = commonModifier,
colors = IconButtonDefaults.filledTonalIconButtonColors(
containerColor = glassColor,
containerColor = GlassTopAppBarDefaults.controlContainerColor(),
contentColor = MaterialTheme.colorScheme.onSurface
)
) {
@@ -79,6 +74,50 @@ fun SmallTopBarButton(
}
}
@Composable
fun TopbarNavigationButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription: String? = "返回",
style: TopBarButtonVariant = TopBarButtonVariant.Filled
) {
TopBarButton(
onClick = onClick,
imageVector = imageVector,
contentDescription = contentDescription,
modifier = modifier.padding(start = 12.dp, end = 4.dp),
style = style
)
}
@Composable
fun TopBarActionButton(
onClick: () -> Unit,
imageVector: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier
) {
val enableProgressive = ThemeConfig.enableProgressiveBlur
if (enableProgressive) {
TopBarButton(
onClick = onClick,
imageVector = imageVector,
contentDescription = contentDescription,
modifier = modifier.padding(end = 12.dp),
style = TopBarButtonVariant.Filled
)
} else {
IconButton(onClick = onClick) {
Icon(
imageVector = imageVector,
contentDescription = contentDescription
)
}
}
}
@Composable
private fun SmallIcon(imageVector: ImageVector, contentDescription: String?) {
AnimatedContent(
@@ -7,12 +7,10 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CardElevation
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import io.legado.app.ui.theme.ThemeState
import io.legado.app.ui.config.themeConfig.ThemeConfig
@Composable
fun GlassCard(
@@ -24,7 +22,7 @@ fun GlassCard(
border: BorderStroke? = null,
content: @Composable ColumnScope.() -> Unit
) {
val opacity by ThemeState.containerOpacity.collectAsState()
val opacity = ThemeConfig.containerOpacity
val cardColors = CardDefaults.cardColors(
containerColor = color.copy(alpha = opacity / 100f)
)
@@ -4,13 +4,21 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MotionScheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.rememberOpaqueColorScheme
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun RoundDropdownMenu(
expanded: Boolean,
@@ -21,17 +29,27 @@ fun RoundDropdownMenu(
verticalSpacing: Dp = 8.dp,
content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit
) {
val colorScheme = rememberOpaqueColorScheme()
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier,
shape = shape,
shadowElevation = shadowElevation
shadowElevation = shadowElevation,
containerColor = colorScheme.surfaceContainerLow
) {
Column(
verticalArrangement = Arrangement.spacedBy(verticalSpacing)
MaterialExpressiveTheme(
colorScheme = colorScheme,
typography = Typography(),
motionScheme = MotionScheme.expressive(),
shapes = Shapes()
) {
content(onDismissRequest)
Column(
verticalArrangement = Arrangement.spacedBy(verticalSpacing)
) {
content(onDismissRequest)
}
}
}
}
@@ -4,26 +4,28 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.rememberOpaqueColorScheme
@Composable
fun RoundDropdownMenuItem(
modifier: Modifier = Modifier,
text: String,
text: @Composable () -> Unit,
onClick: () -> Unit,
trailingIcon: @Composable (() -> Unit)? = null,
) {
val colorScheme = rememberOpaqueColorScheme()
DropdownMenuItem(
modifier = modifier
.padding(horizontal = 8.dp)
.clip(MaterialTheme.shapes.small)
.background(MaterialTheme.colorScheme.surface),
text = { Text(text) },
.background(colorScheme.surface),
text = text,
trailingIcon = trailingIcon,
onClick = onClick,
onClick = onClick
)
}
@@ -15,12 +15,9 @@ import androidx.compose.material3.SheetState
import androidx.compose.material3.Typography
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import io.legado.app.ui.theme.ThemeManager
import io.legado.app.ui.theme.ThemeState
import io.legado.app.ui.theme.rememberOpaqueColorScheme
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
@@ -31,18 +28,7 @@ fun GlassModalBottomSheet(
containerColor: (ColorScheme) -> Color = { it.surfaceContainerLow },
content: @Composable ColumnScope.() -> Unit
) {
val themeMode by ThemeState.themeMode.collectAsState()
val isPureBlack by ThemeState.isPureBlack.collectAsState()
val hasImageBg by ThemeState.hasImageBg.collectAsState()
val paletteStyle by ThemeState.paletteStyle.collectAsState()
val colorScheme = ThemeManager.getColorScheme(
mode = themeMode,
isAmoled = isPureBlack,
isImageBg = hasImageBg,
forceOpaque = true,
paletteStyle = paletteStyle
)
val colorScheme = rememberOpaqueColorScheme()
ModalBottomSheet(
onDismissRequest = onDismissRequest,
@@ -12,6 +12,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.SwipeToDismissBox
import androidx.compose.material3.SwipeToDismissBoxState
import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.rememberSwipeToDismissBoxState
@@ -79,8 +80,8 @@ fun SwipeActionContainer(
SwipeToDismissBoxValue.StartToEnd -> {
startAction?.let { action ->
SwipeBackground(
dismissState = dismissState,
action = action,
progress = progress,
alignStart = true
)
}
@@ -89,8 +90,8 @@ fun SwipeActionContainer(
SwipeToDismissBoxValue.EndToStart -> {
endAction?.let { action ->
SwipeBackground(
dismissState = dismissState,
action = action,
progress = progress,
alignStart = false
)
}
@@ -114,10 +115,13 @@ fun SwipeActionContainer(
@Composable
private fun SwipeBackground(
action: SwipeAction,
progress: Float,
dismissState: SwipeToDismissBoxState,
alignStart: Boolean
) {
val isThresholdReached = progress > 0.6f
val isThresholdReached = dismissState.targetValue == if (alignStart)
SwipeToDismissBoxValue.StartToEnd
else
SwipeToDismissBoxValue.EndToStart
val backgroundColor by animateColorAsState(
targetValue = if (isThresholdReached)
@@ -128,8 +132,8 @@ private fun SwipeBackground(
)
val iconScale by animateFloatAsState(
targetValue = if (isThresholdReached) 1.3f
else progress.coerceIn(0.5f, 1f),
targetValue = if (isThresholdReached) 1.2f
else 1f,
label = "iconScale"
)
@@ -29,7 +29,7 @@ import io.legado.app.ui.widget.components.AdaptiveAnimatedText
import io.legado.app.ui.widget.components.AnimatedTextLine
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.rules.RuleActionState
@@ -75,7 +75,7 @@ fun <T> DynamicTopAppBar(
{ AnimatedTextLine(text = it) }
},
navigationIcon = {
SmallTopBarButton(
TopbarNavigationButton(
onClick = { if (isSelecting) onClearSelection() else onBackClick() },
imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = if (isSelecting) "取消选择" else "返回"