Compare commits

...

5 Commits

Author SHA1 Message Date
dependabot[bot]
5ec97ffc87
Merge e1744cd36e into c2b0c200fb 2024-06-28 08:46:23 +08:00
Horis
c2b0c200fb 优化 2024-06-27 20:33:10 +08:00
Horis
636c369351 优化 2024-06-26 06:36:40 +08:00
Horis
b0f76445f8 优化 2024-06-25 23:07:23 +08:00
dependabot[bot]
e1744cd36e
Bump eslint from 8.57.0 to 9.5.0 in /modules/web
Bumps [eslint](https://github.com/eslint/eslint) from 8.57.0 to 9.5.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.0...v9.5.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 04:27:27 +00:00
12 changed files with 188 additions and 36 deletions

View File

@ -36,7 +36,9 @@ import io.legado.app.utils.ChineseUtils
import io.legado.app.utils.LogUtils
import io.legado.app.utils.defaultSharedPreferences
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.isDebuggable
import kotlinx.coroutines.launch
import org.chromium.base.ThreadUtils
import splitties.init.appCtx
import splitties.systemservices.notificationManager
import java.net.URL
@ -51,6 +53,9 @@ class App : Application() {
super.onCreate()
LogUtils.d("App", "onCreate")
LogUtils.logDeviceInfo()
if (isDebuggable) {
ThreadUtils.setThreadAssertsDisabledForTesting(true)
}
oldConfig = Configuration(resources.configuration)
CrashHandler(this)
//预下载Cronet so

View File

@ -7,12 +7,51 @@ import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.data.dao.*
import io.legado.app.data.entities.*
import io.legado.app.data.dao.BookChapterDao
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 org.intellij.lang.annotations.Language
import splitties.init.appCtx
import java.util.*
import java.util.Locale
val appDb by lazy {
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 BOOK_TABLE_NAME = "books"
const val BOOK_SOURCE_TABLE_NAME = "book_sources"
const val RSS_SOURCE_TABLE_NAME = "rssSources"
val dbCallback = object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {

View File

@ -88,7 +88,7 @@ interface RssSourceDao {
fun flowGroupsUnProcessed(): Flow<List<String>>
@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) <> ''")
val allGroupsUnProcessed: List<String>
@ -142,4 +142,11 @@ interface RssSourceDao {
dealGroups(list)
}.flowOn(IO)
}
fun flowEnabledGroups(): Flow<List<String>> {
return flowEnabledGroupsUnProcessed().map { list ->
dealGroups(list)
}.flowOn(IO)
}
}

View File

@ -7,7 +7,6 @@ import android.view.MenuItem
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.textfield.TextInputLayout
@ -17,6 +16,7 @@ import io.legado.app.constant.AppConst.charsets
import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.data.AppDatabase
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
@ -41,6 +41,7 @@ import io.legado.app.utils.applyTint
import io.legado.app.utils.checkWrite
import io.legado.app.utils.cnCompare
import io.legado.app.utils.enableCustomExport
import io.legado.app.utils.flowWithLifecycleAndDatabaseChange
import io.legado.app.utils.isContentScheme
import io.legado.app.utils.observeEvent
import io.legado.app.utils.parseToUri
@ -221,7 +222,9 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
else -> booksDownload.sortedByDescending { it.durChapterTime }
}
}.flowWithLifecycle(lifecycle).catch {
}.flowWithLifecycleAndDatabaseChange(
lifecycle, table = AppDatabase.BOOK_TABLE_NAME
).catch {
AppLog.put("缓存管理界面获取书籍列表失败\n${it.localizedMessage}", it)
}.flowOn(IO).conflate().collect { books ->
adapter.setItems(books)

View File

@ -5,9 +5,10 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import io.legado.app.R
import io.legado.app.base.adapter.DiffRecyclerAdapter
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.databinding.ItemDownloadBinding
import io.legado.app.help.book.isLocal
@ -16,7 +17,20 @@ import io.legado.app.utils.gone
import io.legado.app.utils.visible
class CacheAdapter(context: Context, private val callBack: CallBack) :
RecyclerAdapter<Book, ItemDownloadBinding>(context) {
DiffRecyclerAdapter<Book, ItemDownloadBinding>(context) {
override val diffItemCallback: DiffUtil.ItemCallback<Book>
get() = object : DiffUtil.ItemCallback<Book>() {
override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean {
return oldItem.bookUrl == newItem.bookUrl
}
override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean {
return oldItem.name == newItem.name
&& oldItem.author == newItem.author
}
}
override fun getViewBinding(parent: ViewGroup): ItemDownloadBinding {
return ItemDownloadBinding.inflate(inflater, parent, false)

View File

@ -16,7 +16,6 @@ import androidx.core.os.bundleOf
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.ItemTouchHelper
@ -25,6 +24,7 @@ import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus
import io.legado.app.data.AppDatabase
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSourcePart
import io.legado.app.databinding.ActivityBookSourceBinding
@ -52,7 +52,8 @@ import io.legado.app.utils.ACache
import io.legado.app.utils.applyTint
import io.legado.app.utils.cnCompare
import io.legado.app.utils.dpToPx
import io.legado.app.utils.flowWithLifecycleFirst
import io.legado.app.utils.flowWithLifecycleAndDatabaseChange
import io.legado.app.utils.flowWithLifecycleAndDatabaseChangeFirst
import io.legado.app.utils.hideSoftInput
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.launch
@ -376,7 +377,10 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
else -> data.reversed()
}
}
}.flowWithLifecycle(lifecycle).catch {
}.flowWithLifecycleAndDatabaseChange(
lifecycle,
table = AppDatabase.BOOK_SOURCE_TABLE_NAME
).catch {
AppLog.put("书源界面更新书源出错", it)
}.flowOn(IO).conflate().collect { data ->
adapter.setItems(data, adapter.diffItemCallback, !Debug.isChecking)
@ -389,8 +393,14 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
private fun initLiveDataGroup() {
lifecycleScope.launch {
appDb.bookSourceDao.flowGroups()
.flowWithLifecycle(lifecycle)
.flowWithLifecycleFirst(groupMenuLifecycleOwner.lifecycle)
.flowWithLifecycleAndDatabaseChange(
lifecycle,
table = AppDatabase.BOOK_SOURCE_TABLE_NAME
)
.flowWithLifecycleAndDatabaseChangeFirst(
groupMenuLifecycleOwner.lifecycle,
table = AppDatabase.BOOK_SOURCE_TABLE_NAME
)
.conflate()
.distinctUntilChanged()
.collect {

View File

@ -15,6 +15,7 @@ import io.legado.app.R
import io.legado.app.base.BaseFragment
import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus
import io.legado.app.data.AppDatabase
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
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.main.MainViewModel
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.setEdgeEffectColor
import io.legado.app.utils.startActivity
@ -175,7 +176,11 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
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)
}.conflate().flowOn(Dispatchers.Default).collect { list ->
binding.tvEmptyMsg.isGone = list.isNotEmpty()

View File

@ -9,13 +9,13 @@ import androidx.appcompat.widget.SearchView
import androidx.core.view.isGone
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppLog
import io.legado.app.data.AppDatabase
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSourcePart
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.utils.applyTint
import io.legado.app.utils.cnCompare
import io.legado.app.utils.flowWithLifecycleAndDatabaseChange
import io.legado.app.utils.setEdgeEffectColor
import io.legado.app.utils.startActivity
import io.legado.app.utils.viewbindingdelegate.viewBinding
@ -39,6 +40,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
@ -126,11 +128,20 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
private fun initGroupData() {
viewLifecycleOwner.lifecycleScope.launch {
appDb.bookSourceDao.flowExploreGroups().conflate().collect {
groups.clear()
groups.addAll(it)
upGroupsMenu()
}
appDb.bookSourceDao.flowExploreGroups()
.flowWithLifecycleAndDatabaseChange(
viewLifecycleOwner.lifecycle,
Lifecycle.State.RESUMED,
AppDatabase.BOOK_SOURCE_TABLE_NAME
)
.conflate()
.distinctUntilChanged()
.collect {
groups.clear()
groups.addAll(it)
upGroupsMenu()
delay(500)
}
}
}
@ -150,7 +161,11 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
else -> {
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)
}.conflate().flowOn(IO).collect {
binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.query.isNotEmpty()

View File

@ -8,12 +8,11 @@ import android.view.View
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.base.VMBaseFragment
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.entities.RssSource
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.utils.applyTint
import io.legado.app.utils.cnCompare
import io.legado.app.utils.flowWithLifecycleAndDatabaseChange
import io.legado.app.utils.openUrl
import io.legado.app.utils.setEdgeEffectColor
import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.startActivity
import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
@ -145,16 +145,17 @@ class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
private fun initGroupData() {
groupsFlowJob?.cancel()
groupsFlowJob = viewLifecycleOwner.lifecycleScope.launch {
appDb.rssSourceDao.flowGroupEnabled().catch {
appDb.rssSourceDao.flowEnabledGroups().catch {
AppLog.put("订阅界面获取分组数据失败\n${it.localizedMessage}", it)
}.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED)
.flowOn(IO).conflate().collect {
groups.clear()
it.map { group ->
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex))
}
upGroupsMenu()
}
}.flowWithLifecycleAndDatabaseChange(
viewLifecycleOwner.lifecycle,
Lifecycle.State.RESUMED,
AppDatabase.RSS_SOURCE_TABLE_NAME
).conflate().collect {
groups.clear()
groups.addAll(it)
upGroupsMenu()
}
}
}
@ -169,7 +170,11 @@ class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
}
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)
}.flowOn(IO).collect {
adapter.setItems(it)

View File

@ -17,6 +17,7 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.content.res.Configuration
@ -384,3 +385,6 @@ val Context.channel: String
}
return ""
}
val Context.isDebuggable: Boolean
get() = applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0

View File

@ -2,6 +2,8 @@ package io.legado.app.utils
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.room.invalidationTrackerFlow
import io.legado.app.data.appDb
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.ensureActive
@ -10,12 +12,14 @@ import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.produceIn
import kotlinx.coroutines.sync.Semaphore
import kotlin.coroutines.coroutineContext
@ -204,3 +208,40 @@ fun <T> Flow<T>.flowWithLifecycleFirst(
}
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()
}

View File

@ -27,7 +27,7 @@
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",
"eslint": "^8.40.0",
"eslint": "^9.5.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.12.0",