[优化] 界面和性能优化
This commit is contained in:
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
|
||||
| ||||