[优化] 界面和性能优化

This commit is contained in:
HapeLee committed 2026-03-05 01:23:47 +08:00
1 parent bede06bf46
commit 16b8164943
37 files changed
+788 -424

No files matched your search

@@ -13,7 +13,7 @@ import io.legado.app.help.http.text
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
import io.legado.app.ui.widget.components.importComponents.ImportItemWrapper
import io.legado.app.ui.widget.components.importComponents.ImportStatus
import io.legado.app.ui.widget.components.rules.RuleActionState
import io.legado.app.ui.widget.components.rules.ListUiState
import io.legado.app.ui.widget.components.rules.SelectableItem
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.isUri
@@ -42,7 +42,7 @@ sealed interface BaseRuleEvent {
) : BaseRuleEvent
}
abstract class BaseRuleViewModel<T : SelectableItem<ID>, Entity, ID, S : RuleActionState<T>>(
abstract class BaseRuleViewModel<T : SelectableItem<ID>, Entity, ID, S : ListUiState<T>>(
application: Application,
initialState: S,
private val uploadRepository: UploadRepository? = null // 设为可空,提高灵活性
@@ -25,6 +25,7 @@ import io.legado.app.data.dao.RssSourceDao
import io.legado.app.data.dao.RssStarDao
import io.legado.app.data.dao.RuleSubDao
import io.legado.app.data.dao.SearchBookDao
import io.legado.app.data.dao.SearchContentHistoryDao
import io.legado.app.data.dao.SearchKeywordDao
import io.legado.app.data.dao.ServerDao
import io.legado.app.data.dao.TxtTocRuleDao
@@ -46,6 +47,7 @@ import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.RssStar
import io.legado.app.data.entities.RuleSub
import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.SearchContentHistory
import io.legado.app.data.entities.SearchKeyword
import io.legado.app.data.entities.Server
import io.legado.app.data.entities.TxtTocRule
@@ -67,14 +69,15 @@ val appDb by lazy {
}
@Database(
version = 83,
version = 85,
exportSchema = true,
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
RssSource::class, Bookmark::class, RssArticle::class,
RssReadRecord::class, ReadRecordDetail::class, ReadRecordSession::class,
RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class, Cache::class,
RuleSub::class, DictRule::class, KeyboardAssist::class, Server::class],
RuleSub::class, DictRule::class, KeyboardAssist::class, Server::class,
SearchContentHistory::class],
views = [BookSourcePart::class],
autoMigrations = [
AutoMigration(from = 43, to = 44),
@@ -115,7 +118,10 @@ val appDb by lazy {
AutoMigration(from = 78, to = 79),
AutoMigration(from = 79, to = 80),
AutoMigration(from = 80, to = 81),
AutoMigration(from = 81, to = 82)
AutoMigration(from = 81, to = 82),
AutoMigration(from = 82, to = 83),
AutoMigration(from = 83, to = 84),
AutoMigration(from = 84, to = 85)
]
)
abstract class AppDatabase : RoomDatabase() {
@@ -141,6 +147,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract val dictRuleDao: DictRuleDao
abstract val keyboardAssistsDao: KeyboardAssistsDao
abstract val serverDao: ServerDao
abstract val searchContentHistoryDao: SearchContentHistoryDao
companion object {
@@ -272,4 +279,4 @@ abstract class AppDatabase : RoomDatabase() {
}
}
}
@@ -6,7 +6,6 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import io.legado.app.data.entities.ReadRecordShow
import io.legado.app.data.entities.readRecord.ReadRecord
import io.legado.app.data.entities.readRecord.ReadRecordDetail
import io.legado.app.data.entities.readRecord.ReadRecordSession
@@ -23,29 +22,9 @@ interface ReadRecordDao {
@get:Query("select * from readRecordSession")
val allSession: List<ReadRecordSession>
@get:Query(
"""
select bookName, bookAuthor, sum(readTime) as readTime, max(lastRead) as lastRead
from readRecord
group by bookName, bookAuthor
order by bookName collate localized, bookAuthor collate localized"""
)
val allShow: List<ReadRecordShow>
@Query("SELECT sum(readTime) FROM readRecord")
fun getTotalReadTime(): Flow<Long?>
@Query(
"""
select bookName, bookAuthor, sum(readTime) as readTime, max(lastRead) as lastRead
from readRecord
where bookName like '%' || :searchKey || '%' or bookAuthor like '%' || :searchKey || '%'
group by bookName, bookAuthor
order by bookName collate localized, bookAuthor collate localized"""
)
fun search(searchKey: String): List<ReadRecordShow>
@Query("select sum(readTime) from readRecord where bookName = :bookName")
fun getReadTime(bookName: String): Long?
@@ -1,6 +1,7 @@
package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
@@ -15,6 +16,7 @@ data class Bookmark(
@PrimaryKey
val time: Long = System.currentTimeMillis(),
val bookName: String = "",
@ColumnInfo(defaultValue = "")
val bookAuthor: String = "",
var chapterIndex: Int = 0,
var chapterPos: Int = 0,
@@ -1,8 +0,0 @@
package io.legado.app.data.entities
data class ReadRecordShow(
var bookName: String,
var bookAuthor: String,
var readTime: Long,
var lastRead: Long
)
@@ -1,5 +1,6 @@
package io.legado.app.data.entities.readRecord
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@@ -10,6 +11,7 @@ data class ReadRecordSession(
val deviceId: String = "",
val bookName: String = "",
@ColumnInfo(defaultValue = "")
val bookAuthor: String = "",
// 一次阅读的开始/结束
@@ -6,10 +6,12 @@ import io.legado.app.data.repository.DirectLinkUploadRepository
import io.legado.app.data.repository.ExploreRepository
import io.legado.app.data.repository.ExploreRepositoryImpl
import io.legado.app.data.repository.ReadRecordRepository
import io.legado.app.data.repository.RemoteBookRepository
import io.legado.app.data.repository.SearchContentRepository
import io.legado.app.data.repository.UploadRepository
import io.legado.app.ui.book.bookmark.AllBookmarkViewModel
import io.legado.app.ui.book.explore.ExploreShowViewModel
import io.legado.app.ui.book.import.remote.RemoteBookViewModel
import io.legado.app.ui.book.readRecord.ReadRecordViewModel
import io.legado.app.ui.book.searchContent.SearchContentViewModel
import io.legado.app.ui.book.toc.TocViewModel
@@ -34,6 +36,7 @@ val appModule = module {
singleOf(::ReadRecordRepository)
singleOf(::BookRepository)
singleOf(::SearchContentRepository)
singleOf(::RemoteBookRepository)
single<UploadRepository> { DirectLinkUploadRepository() }
single<ExploreRepository> { ExploreRepositoryImpl(get()) }
@@ -47,6 +50,7 @@ val appModule = module {
viewModelOf(::TxtTocRuleViewModel)
viewModelOf(::OtherConfigViewModel)
viewModelOf(::TocViewModel)
viewModelOf(::RemoteBookViewModel)
viewModel { (route: ReplaceEditRoute) ->
ReplaceEditViewModel(
@@ -1,142 +0,0 @@
package io.legado.app.ui.book.explore
import android.content.Context
import android.os.Bundle
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.core.view.isVisible
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemSearchBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.model.BookShelfState
import io.legado.app.ui.widget.text.AccentBgTextView
import io.legado.app.utils.dpToPx
import io.legado.app.utils.gone
import io.legado.app.utils.visible
class ExploreShowAdapter(context: Context, val callBack: CallBack) :
RecyclerAdapter<SearchBook, ItemSearchBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemSearchBinding {
return ItemSearchBinding.inflate(inflater, parent, false)
}
override fun convert(
holder: ItemViewHolder,
binding: ItemSearchBinding,
item: SearchBook,
payloads: MutableList<Any>
) {
if (payloads.isEmpty()) {
bind(binding, item)
} else {
for (i in payloads.indices) {
val bundle = payloads[i] as Bundle
bindChange(binding, item, bundle)
}
}
}
private fun bind(binding: ItemSearchBinding, item: SearchBook) {
binding.run {
tvName.text = item.name
tvAuthor.text = context.getString(R.string.author_show, item.author)
when (callBack.getBookShelfState(item.name, item.author, item.bookUrl)) {
BookShelfState.IN_SHELF -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.remove_from_bookshelf)
}
BookShelfState.SAME_NAME_AUTHOR -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.same_name_book)
}
BookShelfState.NOT_IN_SHELF -> {
ivInBookshelf.isVisible = false
}
}
if (item.latestChapterTitle.isNullOrEmpty()) {
tvLasted.gone()
} else {
tvLasted.text = context.getString(R.string.lasted_show, item.latestChapterTitle)
tvLasted.visible()
}
tvIntroduce.text = item.trimIntro(context)
val kinds = item.getKindList()
if (kinds.isEmpty()) {
kindContainer.gone()
} else {
kindContainer.visible()
kindContainer.removeAllViews()
kinds.forEach { kind ->
val kindTextView = AccentBgTextView(context).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
marginEnd = 6.dpToPx()
}
textSize = 11f
text = kind
setLines(1)
setSingleLine(true)
}
kindContainer.addView(kindTextView)
}
}
ivCover.load(
item.coverUrl,
item.name,
item.author,
AppConfig.loadCoverOnlyWifi,
item.origin
)
}
}
private fun bindChange(binding: ItemSearchBinding, item: SearchBook, bundle: Bundle) {
binding.run {
bundle.keySet().forEach {
when (it) {
"isInBookshelf" -> {
when (callBack.getBookShelfState(item.name, item.author, item.bookUrl)) {
BookShelfState.IN_SHELF -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.remove_from_bookshelf)
}
BookShelfState.SAME_NAME_AUTHOR -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.same_name_book)
}
BookShelfState.NOT_IN_SHELF -> {
ivInBookshelf.isVisible = false
}
}
}
}
}
}
}
override fun registerListener(holder: ItemViewHolder, binding: ItemSearchBinding) {
binding.llContent.setOnClickListener {
getItem(holder.layoutPosition)?.let {
callBack.showBookInfo(it.toBook())
}
}
}
interface CallBack {
/**
* 是否已经加入书架
*/
fun getBookShelfState(name: String, author: String, url: String?): BookShelfState
fun showBookInfo(book: Book)
}
}
@@ -52,7 +52,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SheetValue
import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@@ -60,7 +59,6 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -186,12 +184,8 @@ fun ExploreShowScreen(
}
if (showGridCountSheet) {
val sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
GlassModalBottomSheet(
sheetState = sheetState,
modifier = Modifier
.padding(16.dp),
onDismissRequest = { showGridCountSheet = false }
@@ -244,16 +238,8 @@ fun ExploreShowScreen(
}
if (showKindSheet) {
val sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true,
confirmValueChange = { newValue ->
newValue != SheetValue.PartiallyExpanded
}
)
GlassModalBottomSheet(
onDismissRequest = { showKindSheet = false },
sheetState = sheetState,
modifier = Modifier
.heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.72f)
) {
@@ -1,174 +1,372 @@
package io.legado.app.ui.book.import.remote
import android.app.Application
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.Immutable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.Server
import io.legado.app.data.repository.RemoteBookRepository
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.AppWebDav
import io.legado.app.help.config.AppConfig
import io.legado.app.lib.webdav.Authorization
import io.legado.app.model.analyzeRule.CustomUrl
import io.legado.app.model.localBook.LocalBook
import io.legado.app.model.remote.RemoteBook
import io.legado.app.model.remote.RemoteBookWebDav
import io.legado.app.ui.widget.components.rules.InteractionState
import io.legado.app.ui.widget.components.rules.ListUiState
import io.legado.app.ui.widget.components.rules.SelectableItem