This commit is contained in:
Horis
2024-06-25 23:07:23 +08:00
parent b230717bf8
commit 549de44f51
6 changed files with 140 additions and 26 deletions
@@ -7,12 +7,51 @@ import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.data.dao.* import io.legado.app.data.dao.BookChapterDao
import io.legado.app.data.entities.* import io.legado.app.data.dao.BookDao
import io.legado.app.data.dao.BookGroupDao
import io.legado.app.data.dao.BookSourceDao
import io.legado.app.data.dao.BookmarkDao
import io.legado.app.data.dao.CacheDao
import io.legado.app.data.dao.CookieDao
import io.legado.app.data.dao.DictRuleDao
import io.legado.app.data.dao.HttpTTSDao
import io.legado.app.data.dao.KeyboardAssistsDao
import io.legado.app.data.dao.ReadRecordDao
import io.legado.app.data.dao.ReplaceRuleDao
import io.legado.app.data.dao.RssArticleDao
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.SearchKeywordDao
import io.legado.app.data.dao.ServerDao
import io.legado.app.data.dao.TxtTocRuleDao
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookGroup
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.Bookmark
import io.legado.app.data.entities.Cache
import io.legado.app.data.entities.Cookie
import io.legado.app.data.entities.DictRule
import io.legado.app.data.entities.HttpTTS
import io.legado.app.data.entities.KeyboardAssist
import io.legado.app.data.entities.ReadRecord
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssReadRecord
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.SearchKeyword
import io.legado.app.data.entities.Server
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.help.DefaultData import io.legado.app.help.DefaultData
import org.intellij.lang.annotations.Language import org.intellij.lang.annotations.Language
import splitties.init.appCtx import splitties.init.appCtx
import java.util.* import java.util.Locale
val appDb by lazy { val appDb by lazy {
Room.databaseBuilder(appCtx, AppDatabase::class.java, AppDatabase.DATABASE_NAME) Room.databaseBuilder(appCtx, AppDatabase::class.java, AppDatabase.DATABASE_NAME)
@@ -89,6 +128,10 @@ abstract class AppDatabase : RoomDatabase() {
const val DATABASE_NAME = "legado.db" const val DATABASE_NAME = "legado.db"
const val BOOK_TABLE_NAME = "books"
const val BOOK_SOURCE_TABLE_NAME = "book_sources"
const val RSS_SOURCE_TABLE_NAME = "rssSources"
val dbCallback = object : Callback() { val dbCallback = object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) { override fun onCreate(db: SupportSQLiteDatabase) {
@@ -88,7 +88,7 @@ interface RssSourceDao {
fun flowGroupsUnProcessed(): Flow<List<String>> fun flowGroupsUnProcessed(): Flow<List<String>>
@Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> '' and enabled = 1") @Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> '' and enabled = 1")
fun flowGroupEnabled(): Flow<List<String>> fun flowEnabledGroupsUnProcessed(): Flow<List<String>>
@get:Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") @get:Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''")
val allGroupsUnProcessed: List<String> val allGroupsUnProcessed: List<String>
@@ -142,4 +142,11 @@ interface RssSourceDao {
dealGroups(list) dealGroups(list)
}.flowOn(IO) }.flowOn(IO)
} }
fun flowEnabledGroups(): Flow<List<String>> {
return flowEnabledGroupsUnProcessed().map { list ->
dealGroups(list)
}.flowOn(IO)
}
} }
@@ -15,6 +15,7 @@ import io.legado.app.R
import io.legado.app.base.BaseFragment import io.legado.app.base.BaseFragment
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.data.AppDatabase
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
@@ -28,7 +29,7 @@ import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.main.MainViewModel import io.legado.app.ui.main.MainViewModel
import io.legado.app.utils.cnCompare import io.legado.app.utils.cnCompare
import io.legado.app.utils.flowWithLifecycleFirst import io.legado.app.utils.flowWithLifecycleAndDatabaseChangeFirst
import io.legado.app.utils.observeEvent import io.legado.app.utils.observeEvent
import io.legado.app.utils.setEdgeEffectColor import io.legado.app.utils.setEdgeEffectColor
import io.legado.app.utils.startActivity import io.legado.app.utils.startActivity
@@ -175,7 +176,11 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
else -> list.sortedByDescending { it.durChapterTime } else -> list.sortedByDescending { it.durChapterTime }
} }
}.flowWithLifecycleFirst(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch { }.flowWithLifecycleAndDatabaseChangeFirst(
viewLifecycleOwner.lifecycle,
Lifecycle.State.RESUMED,
AppDatabase.BOOK_TABLE_NAME
).catch {
AppLog.put("书架更新出错", it) AppLog.put("书架更新出错", it)
}.conflate().flowOn(Dispatchers.Default).collect { list -> }.conflate().flowOn(Dispatchers.Default).collect { list ->
binding.tvEmptyMsg.isGone = list.isNotEmpty() binding.tvEmptyMsg.isGone = list.isNotEmpty()
@@ -9,13 +9,13 @@ import androidx.appcompat.widget.SearchView
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseFragment import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.data.AppDatabase
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSourcePart import io.legado.app.data.entities.BookSourcePart
import io.legado.app.databinding.FragmentExploreBinding import io.legado.app.databinding.FragmentExploreBinding
@@ -30,6 +30,7 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.main.MainFragmentInterface import io.legado.app.ui.main.MainFragmentInterface
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.cnCompare import io.legado.app.utils.cnCompare
import io.legado.app.utils.flowWithLifecycleAndDatabaseChange
import io.legado.app.utils.setEdgeEffectColor import io.legado.app.utils.setEdgeEffectColor
import io.legado.app.utils.startActivity import io.legado.app.utils.startActivity
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
@@ -126,11 +127,19 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
private fun initGroupData() { private fun initGroupData() {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
appDb.bookSourceDao.flowExploreGroups().conflate().collect { appDb.bookSourceDao.flowExploreGroups()
groups.clear() .flowWithLifecycleAndDatabaseChange(
groups.addAll(it) viewLifecycleOwner.lifecycle,
upGroupsMenu() Lifecycle.State.RESUMED,
} AppDatabase.BOOK_SOURCE_TABLE_NAME
)
.conflate()
.collect {
groups.clear()
groups.addAll(it)
upGroupsMenu()
delay(500)
}
} }
} }
@@ -150,7 +159,11 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
else -> { else -> {
appDb.bookSourceDao.flowExplore(searchKey) appDb.bookSourceDao.flowExplore(searchKey)
} }
}.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch { }.flowWithLifecycleAndDatabaseChange(
viewLifecycleOwner.lifecycle,
Lifecycle.State.RESUMED,
AppDatabase.BOOK_SOURCE_TABLE_NAME
).catch {
AppLog.put("发现界面更新数据出错", it) AppLog.put("发现界面更新数据出错", it)
}.conflate().flowOn(IO).collect { }.conflate().flowOn(IO).collect {
binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.query.isNotEmpty() binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.query.isNotEmpty()
@@ -8,12 +8,11 @@ import android.view.View
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseFragment import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.AppPattern import io.legado.app.data.AppDatabase
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.databinding.FragmentRssBinding import io.legado.app.databinding.FragmentRssBinding
@@ -30,13 +29,14 @@ import io.legado.app.ui.rss.source.manage.RssSourceActivity
import io.legado.app.ui.rss.subscription.RuleSubActivity import io.legado.app.ui.rss.subscription.RuleSubActivity
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.cnCompare import io.legado.app.utils.cnCompare
import io.legado.app.utils.flowWithLifecycleAndDatabaseChange
import io.legado.app.utils.openUrl import io.legado.app.utils.openUrl
import io.legado.app.utils.setEdgeEffectColor import io.legado.app.utils.setEdgeEffectColor
import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.startActivity import io.legado.app.utils.startActivity
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
@@ -145,16 +145,17 @@ class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
private fun initGroupData() { private fun initGroupData() {
groupsFlowJob?.cancel() groupsFlowJob?.cancel()
groupsFlowJob = viewLifecycleOwner.lifecycleScope.launch { groupsFlowJob = viewLifecycleOwner.lifecycleScope.launch {
appDb.rssSourceDao.flowGroupEnabled().catch { appDb.rssSourceDao.flowEnabledGroups().catch {
AppLog.put("订阅界面获取分组数据失败\n${it.localizedMessage}", it) AppLog.put("订阅界面获取分组数据失败\n${it.localizedMessage}", it)
}.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED) }.flowWithLifecycleAndDatabaseChange(
.flowOn(IO).conflate().collect { viewLifecycleOwner.lifecycle,
groups.clear() Lifecycle.State.RESUMED,
it.map { group -> AppDatabase.RSS_SOURCE_TABLE_NAME
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) ).conflate().collect {
} groups.clear()
upGroupsMenu() groups.addAll(it)
} upGroupsMenu()
}
} }
} }
@@ -169,7 +170,11 @@ class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
} }
else -> appDb.rssSourceDao.flowEnabled(searchKey) else -> appDb.rssSourceDao.flowEnabled(searchKey)
}.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch { }.flowWithLifecycleAndDatabaseChange(
viewLifecycleOwner.lifecycle,
Lifecycle.State.RESUMED,
AppDatabase.RSS_SOURCE_TABLE_NAME
).catch {
AppLog.put("订阅界面更新数据出错", it) AppLog.put("订阅界面更新数据出错", it)
}.flowOn(IO).collect { }.flowOn(IO).collect {
adapter.setItems(it) adapter.setItems(it)
@@ -2,6 +2,8 @@ package io.legado.app.utils
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.room.invalidationTrackerFlow
import io.legado.app.data.appDb
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
@@ -10,12 +12,14 @@ import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapMerge import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.produceIn
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlin.coroutines.coroutineContext import kotlin.coroutines.coroutineContext
@@ -204,3 +208,40 @@ fun <T> Flow<T>.flowWithLifecycleFirst(
} }
close() close()
} }
fun <T> Flow<T>.flowWithLifecycleAndDatabaseChange(
lifecycle: Lifecycle,
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
table: String
): Flow<T> = callbackFlow {
val channel = appDb.invalidationTrackerFlow(table)
.conflate()
.produceIn(this)
lifecycle.repeatOnLifecycle(minActiveState) {
channel.receive()
this@flowWithLifecycleAndDatabaseChange.collect {
send(it)
}
}
close()
}
fun <T> Flow<T>.flowWithLifecycleAndDatabaseChangeFirst(
lifecycle: Lifecycle,
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
table: String
): Flow<T> = callbackFlow {
if (!lifecycle.currentState.isAtLeast(minActiveState)) {
send(first())
}
val channel = appDb.invalidationTrackerFlow(table)
.conflate()
.produceIn(this)
lifecycle.repeatOnLifecycle(minActiveState) {
channel.receive()
this@flowWithLifecycleAndDatabaseChangeFirst.collect {
send(it)
}
}
close()
}