From 8956f1d682cca720d2e89e0e3d7da4efac39fa21 Mon Sep 17 00:00:00 2001 From: HapeLee <1321903405@qq.com> Date: Thu, 26 Feb 2026 01:22:46 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E9=83=A8=E5=88=86=20Sigma=20=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=E4=B9=A6=E6=BA=90=E5=9B=9E=E8=B0=83=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/model/AudioPlay.kt | 2 + .../io/legado/app/model/SourceCallBack.kt | 4 +- .../app/ui/book/info/BookInfoActivity.kt | 55 +++++++++++++++++-- .../io/legado/app/ui/main/MainViewModel.kt | 26 ++++++++- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/AudioPlay.kt b/app/src/main/java/io/legado/app/model/AudioPlay.kt index 6749b8660..a7cd36e93 100644 --- a/app/src/main/java/io/legado/app/model/AudioPlay.kt +++ b/app/src/main/java/io/legado/app/model/AudioPlay.kt @@ -102,6 +102,7 @@ object AudioPlay : CoroutineScope by MainScope() { durPlayUrl = "" durAudioSize = 0 upDurChapter() + SourceCallBack.callBackBook(SourceCallBack.START_READ, bookSource, book, durChapter) postEvent(EventBus.AUDIO_BUFFER_PROGRESS, 0) } @@ -360,6 +361,7 @@ object AudioPlay : CoroutineScope by MainScope() { ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(), book.getUseReplaceRule() ) + SourceCallBack.callBackBook(SourceCallBack.SAVE_READ, bookSource, book, it) } } book.update() diff --git a/app/src/main/java/io/legado/app/model/SourceCallBack.kt b/app/src/main/java/io/legado/app/model/SourceCallBack.kt index 0db7a3d48..498e84d1c 100644 --- a/app/src/main/java/io/legado/app/model/SourceCallBack.kt +++ b/app/src/main/java/io/legado/app/model/SourceCallBack.kt @@ -56,7 +56,7 @@ object SourceCallBack { } activity.lifecycleScope.launch(IO) { val java = SourceLoginJsExtensions(activity, source) - runCatching { + kotlin.runCatching { val result = runScriptWithContext { source.evalJS(jsStr) { put("event", event) @@ -114,7 +114,7 @@ object SourceCallBack { val jsStr = source.getContentRule().callBackJs if (jsStr.isNullOrEmpty()) return scope.launch(IO) { - runCatching { + kotlin.runCatching { withTimeout(30000L) { runScriptWithContext { source.evalJS(jsStr) { diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index 12997ea1f..bfebcc6f1 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -59,6 +59,7 @@ import io.legado.app.help.config.LocalConfig import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.selector import io.legado.app.model.BookCover +import io.legado.app.model.SourceCallBack import io.legado.app.model.remote.RemoteBookWebDav import io.legado.app.ui.about.AppLogDialog import io.legado.app.ui.book.audio.AudioPlayActivity @@ -828,15 +829,47 @@ class BookInfoActivity : } tvAuthor.setOnClickListener { viewModel.getBook(false)?.let { book -> - startActivity { - putExtra("key", book.author) + SourceCallBack.callBackBtn( + this@BookInfoActivity, + SourceCallBack.CLICK_AUTHOR, + viewModel.bookSource, + book, + null + ) { + startActivity { + putExtra("key", book.author) + } } } } + tvAuthor.setOnLongClickListener { + viewModel.getBook(false)?.let { book -> + SourceCallBack.callBackBtn( + this@BookInfoActivity, + SourceCallBack.LONG_CLICK_AUTHOR, + viewModel.bookSource, + book, + null + ) { + startActivity { + putExtra("key", book.author) + } + } + } + true + } tvName.setOnClickListener { viewModel.getBook(false)?.let { book -> - startActivity { - putExtra("key", book.name) + SourceCallBack.callBackBtn( + this@BookInfoActivity, + SourceCallBack.CLICK_BOOK_NAME, + viewModel.bookSource, + book, + null + ) { + startActivity { + putExtra("key", book.name) + } } } } @@ -922,14 +955,14 @@ class BookInfoActivity : @SuppressLint("InflateParams") private fun deleteBook() { - viewModel.getBook()?.let { + viewModel.getBook()?.let { book -> if (LocalConfig.bookInfoDeleteAlert) { alert( titleResource = R.string.draw, messageResource = R.string.sure_del ) { var checkBox: CheckBox? = null - if (it.isLocal) { + if (book.isLocal) { checkBox = CheckBox(this@BookInfoActivity).apply { setText(R.string.delete_book_file) isChecked = LocalConfig.deleteBookOriginal @@ -944,6 +977,11 @@ class BookInfoActivity : if (checkBox != null) { LocalConfig.deleteBookOriginal = checkBox.isChecked } + SourceCallBack.callBackBook( + SourceCallBack.DEL_BOOK_SHELF, + viewModel.bookSource, + book + ) //确认后删除书架 viewModel.delBook(LocalConfig.deleteBookOriginal) { setResult(RESULT_OK) finish() @@ -952,6 +990,11 @@ class BookInfoActivity : noButton() } } else { + SourceCallBack.callBackBook( + SourceCallBack.DEL_BOOK_SHELF, + viewModel.bookSource, + book + ) //点按钮直接删除书架 viewModel.delBook(LocalConfig.deleteBookOriginal) { setResult(RESULT_OK) finish() diff --git a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt index 17540fe17..ffa490ee2 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt @@ -23,6 +23,7 @@ import io.legado.app.help.book.sync import io.legado.app.help.config.AppConfig import io.legado.app.model.CacheBook import io.legado.app.model.ReadBook +import io.legado.app.model.SourceCallBack import io.legado.app.model.webBook.WebBook import io.legado.app.service.CacheBookService import io.legado.app.utils.onEachParallel @@ -53,6 +54,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) { val onUpBooksLiveData = MutableLiveData() private var upTocJob: Job? = null private var cacheBookJob: Job? = null + private val eventListenerSource = ConcurrentHashMap() val booksListRecycledViewPool = RecycledViewPool().apply { setMaxRecycledViews(0, 30) } @@ -156,6 +158,17 @@ class MainViewModel(application: Application) : BaseViewModel(application) { } return } + if (source.eventListener) { + // 使用 putIfAbsent 确保只添加一次 + if (eventListenerSource.putIfAbsent(source, true) == null) { + // 通知监听事件的书源,书架刷新开始 + SourceCallBack.callBackSource( + viewModelScope, + SourceCallBack.START_SHELF_REFRESH, + source + ) + } + } kotlin.runCatching { val oldBook = book.copy() if (book.tocUrl.isBlank()) { @@ -210,13 +223,24 @@ class MainViewModel(application: Application) : BaseViewModel(application) { * 缓存书籍 */ private fun cacheBook() { + //开始缓存前,通知监听事件的书源,书架刷新已完成 + eventListenerSource.toList().forEach { + SourceCallBack.callBackSource( + viewModelScope, + SourceCallBack.END_SHELF_REFRESH, + it.first + ) + } + eventListenerSource.clear() if (AppConfig.preDownloadNum == 0) return cacheBookJob?.cancel() cacheBookJob = viewModelScope.launch(upTocPool) { launch { while (isActive && CacheBook.isRun) { + val isOnUpTocBooksEmpty = + onUpTocBooks.isEmpty() //有目录更新是不缓存,优先更新目录,现在更多网站限制并发 - CacheBook.setWorkingState(waitUpTocBooks.isEmpty() && onUpTocBooks.isEmpty()) + CacheBook.setWorkingState(waitUpTocBooks.isEmpty() && isOnUpTocBooksEmpty) delay(1000) } }