diff --git a/app/src/main/java/io/legado/app/model/ReadBook.kt b/app/src/main/java/io/legado/app/model/ReadBook.kt index 936334982..7a591e74b 100644 --- a/app/src/main/java/io/legado/app/model/ReadBook.kt +++ b/app/src/main/java/io/legado/app/model/ReadBook.kt @@ -2,7 +2,11 @@ package io.legado.app.model import io.legado.app.constant.AppLog import io.legado.app.data.appDb -import io.legado.app.data.entities.* +import io.legado.app.data.entities.Book +import io.legado.app.data.entities.BookChapter +import io.legado.app.data.entities.BookProgress +import io.legado.app.data.entities.BookSource +import io.legado.app.data.entities.ReadRecord import io.legado.app.help.AppWebDav import io.legado.app.help.book.BookHelp import io.legado.app.help.book.ContentProcessor @@ -88,7 +92,7 @@ object ReadBook : CoroutineScope by MainScope() { upWebBook(book) lastBookPress = null webBookProgress = null - TextFile.txtBuffer = null + TextFile.clear() synchronized(this) { loadingChapters.clear() } diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index 58a214cd9..faac01d3c 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -82,7 +82,7 @@ object LocalBook { } } - @Throws(Exception::class) + @Throws(TocEmptyException::class) fun getChapterList(book: Book): ArrayList { val chapters = when { book.isEpub -> { diff --git a/app/src/main/java/io/legado/app/model/localBook/TextFile.kt b/app/src/main/java/io/legado/app/model/localBook/TextFile.kt index f45a4daf9..f1c4d4cda 100644 --- a/app/src/main/java/io/legado/app/model/localBook/TextFile.kt +++ b/app/src/main/java/io/legado/app/model/localBook/TextFile.kt @@ -16,56 +16,37 @@ import java.util.regex.Matcher import java.util.regex.Pattern import kotlin.math.min -class TextFile(private val book: Book) { +class TextFile(private var book: Book) { + @Suppress("ConstPropertyName") companion object { private val padRegex = "^[\\n\\s]+".toRegex() - private const val bufferSize = 8 * 1024 * 1024 - private var bufferStart = -1 - private var bufferEnd = -1 - var txtBuffer: ByteArray? = null - var bookUrl = "" + private const val txtBufferSize = 8 * 1024 * 1024 + private var textFile: TextFile? = null + + @Synchronized + private fun getTextFile(book: Book): TextFile { + if (textFile == null || textFile?.book?.bookUrl != book.bookUrl) { + textFile = TextFile(book) + return textFile!! + } + textFile?.book = book + return textFile!! + } @Throws(FileNotFoundException::class) fun getChapterList(book: Book): ArrayList { - return TextFile(book).getChapterList() + return getTextFile(book).getChapterList() } @Synchronized @Throws(FileNotFoundException::class) fun getContent(book: Book, bookChapter: BookChapter): String { - if (txtBuffer == null || bookUrl != book.bookUrl || bookChapter.start!! > bufferEnd || bookChapter.end!! < bufferStart) { - bookUrl = book.bookUrl - LocalBook.getBookInputStream(book).use { bis -> - bufferStart = bufferSize * (bookChapter.start!! / bufferSize).toInt() - txtBuffer = ByteArray(min(bufferSize, bis.available() - bufferStart)) - bufferEnd = bufferStart + txtBuffer!!.size - bis.skip(bufferStart.toLong()) - bis.read(txtBuffer) - } - } + return getTextFile(book).getContent(bookChapter) + } - val count = (bookChapter.end!! - bookChapter.start!!).toInt() - val buffer = ByteArray(count) - - if (bookChapter.start!! < bufferEnd && bookChapter.end!! > bufferEnd || bookChapter.start!! < bufferStart && bookChapter.end!! > bufferStart) { - /** 章节内容在缓冲区交界处 */ - LocalBook.getBookInputStream(book).use { bis -> - bis.skip(bookChapter.start!!) - bis.read(buffer) - } - } else { - /** 章节内容在缓冲区内 */ - txtBuffer!!.copyInto( - buffer, - 0, - (bookChapter.start!! - bufferStart).toInt(), - (bookChapter.end!! - bufferStart).toInt() - ) - } - - return String(buffer, book.fileCharset()).substringAfter(bookChapter.title) - .replace(padRegex, "  ") + fun clear() { + textFile = null } } @@ -83,10 +64,14 @@ class TextFile(private val book: Book) { private var charset: Charset = book.fileCharset() + private var txtBuffer: ByteArray? = null + private var bufferStart = -1L + private var bufferEnd = -1L + /** * 获取目录 */ - @Throws(FileNotFoundException::class) + @Throws(FileNotFoundException::class, SecurityException::class, EmptyFileException::class) fun getChapterList(): ArrayList { if (book.charset == null || book.tocUrl.isBlank()) { LocalBook.getBookInputStream(book).use { bis -> @@ -112,6 +97,43 @@ class TextFile(private val book: Book) { return toc } + fun getContent(chapter: BookChapter): String { + val start = chapter.start!! + val end = chapter.end!! + if (txtBuffer == null || start > bufferEnd || end < bufferStart) { + LocalBook.getBookInputStream(book).use { bis -> + bufferStart = txtBufferSize * (start / txtBufferSize) + txtBuffer = ByteArray(min(txtBufferSize, bis.available() - bufferStart.toInt())) + bufferEnd = bufferStart + txtBuffer!!.size + bis.skip(bufferStart) + bis.read(txtBuffer) + } + } + + val count = (end - start).toInt() + val buffer = ByteArray(count) + + if (bufferEnd in start..end || bufferStart in start..end) { + /** 章节内容在缓冲区交界处 */ + LocalBook.getBookInputStream(book).use { bis -> + bis.skip(start) + bis.read(buffer) + } + } else { + /** 章节内容在缓冲区内 */ + txtBuffer!!.copyInto( + buffer, + 0, + (start - bufferStart).toInt(), + (end - bufferStart).toInt() + ) + } + + return String(buffer, charset) + .substringAfter(chapter.title) + .replace(padRegex, "  ") + } + /** * 按规则解析目录 */ @@ -162,7 +184,7 @@ class TextFile(private val book: Book) { val chapterStart = matcher.start() //获取章节内容 val chapterContent = blockContent.substring(seekPos, chapterStart) - val chapterLength = chapterContent.toByteArray(charset).size + val chapterLength = chapterContent.toByteArray(charset).size.toLong() val lastStart = toc.lastOrNull()?.start ?: curOffset if (book.getSplitLongChapter() && curOffset + chapterLength - lastStart > maxLengthWithToc) { toc.lastOrNull()?.let { @@ -186,7 +208,7 @@ class TextFile(private val book: Book) { curChapter.start = curOffset + chapterLength toc.add(curChapter) } else if (seekPos == 0 && chapterStart != 0) { - /* + /** * 如果 seekPos == 0 && chapterStart != 0 表示当前block处前面有一段内容 * 第一种情况一定是序章 第二种情况是上一个章节的内容 */ @@ -196,13 +218,18 @@ class TextFile(private val book: Book) { val qyChapter = BookChapter() qyChapter.title = "前言" qyChapter.start = curOffset - qyChapter.end = curOffset + chapterLength.toLong() + qyChapter.end = curOffset + chapterLength toc.add(qyChapter) + book.intro = if (chapterContent.length <= 500) { + chapterContent + } else { + chapterContent.substring(0, 500) + } } //创建当前章节 val curChapter = BookChapter() curChapter.title = matcher.group() - curChapter.start = curOffset + chapterLength.toLong() + curChapter.start = curOffset + chapterLength toc.add(curChapter) } else { //否则就block分割之后,上一个章节的剩余内容 //获取上一章节 @@ -210,7 +237,7 @@ class TextFile(private val book: Book) { lastChapter.isVolume = chapterContent.substringAfter(lastChapter.title).isBlank() //将当前段落添加上一章去 - lastChapter.end = lastChapter.end!! + chapterLength.toLong() + lastChapter.end = lastChapter.end!! + chapterLength //创建当前章节 val curChapter = BookChapter() curChapter.title = matcher.group() @@ -224,7 +251,7 @@ class TextFile(private val book: Book) { lastChapter.isVolume = chapterContent.substringAfter(lastChapter.title).isBlank() lastChapter.end = - lastChapter.start!! + chapterContent.toByteArray(charset).size.toLong() + lastChapter.start!! + chapterLength //创建当前章节 val curChapter = BookChapter() curChapter.title = matcher.group() diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt index d2b1fdcf7..161a98c38 100644 --- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt @@ -1,45 +1,27 @@ package io.legado.app.ui.book.explore import android.os.Bundle -import android.view.Menu -import android.view.MenuItem import androidx.activity.viewModels import androidx.recyclerview.widget.RecyclerView import io.legado.app.R import io.legado.app.base.VMBaseActivity -import io.legado.app.constant.AppLog -import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.SearchBook import io.legado.app.databinding.ActivityExploreShowBinding -import io.legado.app.databinding.DialogPageChoiceBinding import io.legado.app.databinding.ViewLoadMoreBinding -import io.legado.app.help.coroutine.Coroutine -import io.legado.app.lib.dialogs.alert -import io.legado.app.lib.theme.backgroundColor -import io.legado.app.model.webBook.WebBook -import io.legado.app.ui.book.group.GroupSelectDialog import io.legado.app.ui.book.info.BookInfoActivity -import io.legado.app.ui.widget.dialog.WaitDialog import io.legado.app.ui.widget.recycler.LoadMoreView import io.legado.app.ui.widget.recycler.VerticalDivider -import io.legado.app.utils.showDialogFragment import io.legado.app.utils.startActivity import io.legado.app.utils.viewbindingdelegate.viewBinding -import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.launch class ExploreShowActivity : VMBaseActivity(), - ExploreShowAdapter.CallBack, - GroupSelectDialog.CallBack { + ExploreShowAdapter.CallBack { override val binding by viewBinding(ActivityExploreShowBinding::inflate) override val viewModel by viewModels() private val adapter by lazy { ExploreShowAdapter(this, this) } private val loadMoreView by lazy { LoadMoreView(this) } - private val waitDialog by lazy { - WaitDialog(this) - } override fun onActivityCreated(savedInstanceState: Bundle?) { binding.titleBar.title = intent.getStringExtra("exploreName") @@ -116,84 +98,4 @@ class ExploreShowActivity : VMBaseActivity addAllToBookshelf() - } - return super.onCompatOptionsItemSelected(item) - } - - private fun addAllToBookshelf() { - showDialogFragment(GroupSelectDialog(0)) - } - - override fun upGroup(requestCode: Int, groupId: Long) { - - alert("选择页数范围") { - val alertBinding = DialogPageChoiceBinding.inflate(layoutInflater).apply { - root.setBackgroundColor(root.context.backgroundColor) - } - customView { alertBinding.root } - yesButton { - alertBinding.run { - val start = editStart.text - .runCatching { - toString().toInt() - }.getOrDefault(1) - val end = editEnd.text - .runCatching { - toString().toInt() - }.getOrDefault(9) - addAllToBookshelf(start, end, groupId) - } - } - noButton() - } - - } - - private fun addAllToBookshelf(start: Int, end: Int, groupId: Long) { - val job = Coroutine.async { - launch(Main) { - waitDialog.setText("加载列表中...") - waitDialog.show() - } - val searchBooks = viewModel.loadExploreBooks(start, end) - val books = searchBooks.map { - it.toBook() - } - launch(Main) { - waitDialog.setText("添加书架中...") - } - books.forEach { - appDb.bookDao.getBook(it.bookUrl)?.let { book -> - book.group = book.group or groupId - it.order = appDb.bookDao.minOrder - 1 - book.save() - return@forEach - } - if (it.tocUrl.isEmpty()) { - val source = appDb.bookSourceDao.getBookSource(it.origin)!! - WebBook.getBookInfoAwait(source, it) - } - it.order = appDb.bookDao.minOrder - 1 - it.group = groupId - it.save() - } - }.onError { - AppLog.put("添加书架出错\n${it.localizedMessage}", it) - }.onFinally { - waitDialog.dismiss() - } - waitDialog.setOnCancelListener { - job.cancel() - } - } - } diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt index 734f343db..645156338 100644 --- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt @@ -70,18 +70,4 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application } } - suspend fun loadExploreBooks(start: Int, end: Int): List { - val source = bookSource - val url = exploreUrl - if (source == null || url == null) return emptyList() - val searchBooks = arrayListOf() - for (page in start..end) { - val books = WebBook.exploreBookAwait(source, url, page) - if (books.isEmpty()) break - searchBooks.addAll(books) - } - searchBooks.reverse() - return searchBooks - } - } diff --git a/app/src/main/res/layout/dialog_page_choice.xml b/app/src/main/res/layout/dialog_page_choice.xml deleted file mode 100644 index 597f253b1..000000000 --- a/app/src/main/res/layout/dialog_page_choice.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/main/res/menu/explore_show.xml b/app/src/main/res/menu/explore_show.xml deleted file mode 100644 index 754934b10..000000000 --- a/app/src/main/res/menu/explore_show.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index a535adb05..e44a97a2a 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -1117,8 +1117,6 @@ 测试 显示等待更新数量 退出软件 - 全部加入书架 - 页至 Analyzed Comprehensive 起效的替换 diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 2139cb703..e8e131884 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -1120,8 +1120,6 @@ 测试 显示等待更新数量 退出软件 - 全部加入书架 - 页至 Analyzed Comprehensive 起效的替换 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index cafde6dcf..3cb4b18aa 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1120,8 +1120,6 @@ 测试 显示等待更新数量 退出软件 - 全部加入书架 - 页至 Analyzed Comprehensive 起效的替换 diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index fb633eac9..d5162fca5 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1116,8 +1116,6 @@ Còn Kiểm tra Hiển thị số bản cập nhật đang đợi Thoát khỏi phần mềm - Thêm tất cả vào giá sách - Trang tới Analyzed Comprehensive 起效的替换 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 701b26050..900603145 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -1117,8 +1117,6 @@ 测试 显示等待更新数量 退出软件 - 全部加入书架 - 页至 解析示例 綜合排序 起效的替换 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index b906e44cf..7e899fbfe 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1119,8 +1119,6 @@ 测试 显示等待更新数量 退出软件 - 全部加入书架 - 页至 解析示例 綜合排序 起效的替换 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 2d1547e04..5ca353c9d 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1119,8 +1119,6 @@ 测试 显示等待更新数量 退出软件 - 全部加入书架 - 页至 解析示例 综合排序 起效的替换 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f43c806bb..44f1a448d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1120,8 +1120,6 @@ Test Display the number of pending updates Exit - 全部加入书架 - 页至 Analyzed Comprehensive Effective replacement