优化
This commit is contained in:
@@ -12,6 +12,11 @@
|
||||
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
|
||||
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
||||
|
||||
**2023/06/07**
|
||||
|
||||
* 修复长按菜单全文搜索结果不全或无结果问题
|
||||
* 优化全文搜索速度
|
||||
|
||||
**2023/06/06**
|
||||
|
||||
* 更新cronet: 114.0.5735.57
|
||||
|
||||
@@ -18,7 +18,7 @@ object AppPattern {
|
||||
val authorRegex = Regex("^\\s*作\\s*者[::\\s]+|\\s+著")
|
||||
val fileNameRegex = Regex("[\\\\/:*?\"<>|.]")
|
||||
val splitGroupRegex = Regex("[,;,;]")
|
||||
val titleNumPattern = Pattern.compile("(第)(.+?)(章)")
|
||||
val titleNumPattern: Pattern = Pattern.compile("(第)(.+?)(章)")
|
||||
|
||||
//书源调试信息中的各种符号
|
||||
val debugMessageSymbolRegex = Regex("[⇒◇┌└≡]")
|
||||
@@ -48,4 +48,8 @@ object AppPattern {
|
||||
val semicolonRegex = ";".toRegex()
|
||||
|
||||
val equalsRegex = "=".toRegex()
|
||||
|
||||
val spaceRegex = "\\s+".toRegex()
|
||||
|
||||
val regexCharRegex = "[{}()\\[\\].+*?^$\\\\|]".toRegex()
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@ interface BookDao {
|
||||
@Query("SELECT * FROM books WHERE name = :name and author = :author")
|
||||
fun getBook(name: String, author: String): Book?
|
||||
|
||||
@Query("SELECT * FROM books WHERE name = :name and origin = :origin")
|
||||
fun getBookByOrigin(name: String, origin: String): Book?
|
||||
|
||||
@get:Query("select count(bookUrl) from books where (SELECT sum(groupId) FROM book_groups)")
|
||||
val noGroupSize: Int
|
||||
|
||||
|
||||
@@ -135,6 +135,10 @@ data class Book(
|
||||
@IgnoredOnParcel
|
||||
var downloadUrls: List<String>? = null
|
||||
|
||||
@Ignore
|
||||
@IgnoredOnParcel
|
||||
private var folderName: String? = null
|
||||
|
||||
fun getRealAuthor() = author.replace(AppPattern.authorRegex, "")
|
||||
|
||||
fun getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0)
|
||||
@@ -237,10 +241,18 @@ data class Book(
|
||||
}
|
||||
|
||||
fun getFolderName(): String {
|
||||
folderName?.let {
|
||||
return it
|
||||
}
|
||||
//防止书名过长,只取9位
|
||||
var folderName = name.replace(AppPattern.fileNameRegex, "")
|
||||
folderName = folderName.substring(0, min(9, folderName.length))
|
||||
return folderName + MD5Utils.md5Encode16(bookUrl)
|
||||
folderName = getFolderNameNoCache()
|
||||
return folderName!!
|
||||
}
|
||||
|
||||
fun getFolderNameNoCache(): String {
|
||||
return name.replace(AppPattern.fileNameRegex, "").let {
|
||||
it.substring(0, min(9, it.length)) + MD5Utils.md5Encode16(bookUrl)
|
||||
}
|
||||
}
|
||||
|
||||
fun toSearchBook() = SearchBook(
|
||||
|
||||
@@ -59,6 +59,12 @@ data class BookChapter(
|
||||
GSON.fromJsonObject<HashMap<String, String>>(variable).getOrNull() ?: hashMapOf()
|
||||
}
|
||||
|
||||
@delegate:Ignore
|
||||
@IgnoredOnParcel
|
||||
private val titleMD5: String by lazy {
|
||||
MD5Utils.md5Encode16(title)
|
||||
}
|
||||
|
||||
override fun putVariable(key: String, value: String?): Boolean {
|
||||
if (super.putVariable(key, value)) {
|
||||
variable = GSON.toJson(variableMap)
|
||||
@@ -145,9 +151,10 @@ data class BookChapter(
|
||||
|
||||
@Suppress("unused")
|
||||
fun getFileName(suffix: String = "nb"): String =
|
||||
String.format("%05d-%s.%s", index, MD5Utils.md5Encode16(title), suffix)
|
||||
String.format("%05d-%s.%s", index, titleMD5, suffix)
|
||||
|
||||
|
||||
@Suppress("unused")
|
||||
fun getFontName(): String = String.format("%05d-%s.ttf", index, MD5Utils.md5Encode16(title))
|
||||
fun getFontName(): String = String.format("%05d-%s.ttf", index, titleMD5)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,8 +51,19 @@ object BookHelp {
|
||||
}
|
||||
|
||||
fun updateCacheFolder(oldBook: Book, newBook: Book) {
|
||||
val oldFolderPath = FileUtils.getPath(downloadDir, cacheFolderName, oldBook.getFolderName())
|
||||
val newFolderPath = FileUtils.getPath(downloadDir, cacheFolderName, newBook.getFolderName())
|
||||
val oldFolderName = oldBook.getFolderNameNoCache()
|
||||
val newFolderName = newBook.getFolderNameNoCache()
|
||||
if (oldFolderName == newFolderName) return
|
||||
val oldFolderPath = FileUtils.getPath(
|
||||
downloadDir,
|
||||
cacheFolderName,
|
||||
oldFolderName
|
||||
)
|
||||
val newFolderPath = FileUtils.getPath(
|
||||
downloadDir,
|
||||
cacheFolderName,
|
||||
newFolderName
|
||||
)
|
||||
FileUtils.move(oldFolderPath, newFolderPath)
|
||||
}
|
||||
|
||||
@@ -331,21 +342,25 @@ object BookHelp {
|
||||
* 设置是否禁用正文的去除重复标题,针对单个章节
|
||||
*/
|
||||
fun setRemoveSameTitle(book: Book, bookChapter: BookChapter, removeSameTitle: Boolean) {
|
||||
val fileName = bookChapter.getFileName("nr")
|
||||
val contentProcessor = ContentProcessor.get(book)
|
||||
if (removeSameTitle) {
|
||||
val path = FileUtils.getPath(
|
||||
downloadDir,
|
||||
cacheFolderName,
|
||||
book.getFolderName(),
|
||||
bookChapter.getFileName(".nr")
|
||||
fileName
|
||||
)
|
||||
contentProcessor.removeSameTitleCache.remove(fileName)
|
||||
File(path).delete()
|
||||
} else {
|
||||
FileUtils.createFileIfNotExist(
|
||||
downloadDir,
|
||||
cacheFolderName,
|
||||
book.getFolderName(),
|
||||
bookChapter.getFileName(".nr")
|
||||
fileName
|
||||
)
|
||||
contentProcessor.removeSameTitleCache.add(fileName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +372,7 @@ object BookHelp {
|
||||
downloadDir,
|
||||
cacheFolderName,
|
||||
book.getFolderName(),
|
||||
bookChapter.getFileName(".nr")
|
||||
bookChapter.getFileName("nr")
|
||||
)
|
||||
return !File(path).exists()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.legado.app.help.book
|
||||
|
||||
import com.github.liuyueyi.quick.transfer.ChineseUtils
|
||||
import io.legado.app.constant.AppLog
|
||||
import io.legado.app.constant.AppPattern.spaceRegex
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.Book
|
||||
import io.legado.app.data.entities.BookChapter
|
||||
@@ -9,6 +10,7 @@ import io.legado.app.data.entities.ReplaceRule
|
||||
import io.legado.app.exception.RegexTimeoutException
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.utils.escapeRegex
|
||||
import io.legado.app.utils.replace
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
import io.legado.app.utils.toastOnUi
|
||||
@@ -25,7 +27,8 @@ class ContentProcessor private constructor(
|
||||
|
||||
companion object {
|
||||
private val processors = hashMapOf<String, WeakReference<ContentProcessor>>()
|
||||
var enableRemoveSameTitle = true
|
||||
|
||||
fun get(book: Book) = get(book.name, book.origin)
|
||||
|
||||
fun get(bookName: String, bookOrigin: String): ContentProcessor {
|
||||
val processorWr = processors[bookName + bookOrigin]
|
||||
@@ -47,9 +50,11 @@ class ContentProcessor private constructor(
|
||||
|
||||
private val titleReplaceRules = CopyOnWriteArrayList<ReplaceRule>()
|
||||
private val contentReplaceRules = CopyOnWriteArrayList<ReplaceRule>()
|
||||
val removeSameTitleCache = hashSetOf<String>()
|
||||
|
||||
init {
|
||||
upReplaceRules()
|
||||
upRemoveSameTitle()
|
||||
}
|
||||
|
||||
fun upReplaceRules() {
|
||||
@@ -63,6 +68,15 @@ class ContentProcessor private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun upRemoveSameTitle() {
|
||||
val book = appDb.bookDao.getBookByOrigin(bookName, bookOrigin) ?: return
|
||||
removeSameTitleCache.clear()
|
||||
val files = BookHelp.getChapterFiles(book).filter {
|
||||
it.endsWith("nr")
|
||||
}
|
||||
removeSameTitleCache.addAll(files)
|
||||
}
|
||||
|
||||
fun getTitleReplaceRules(): List<ReplaceRule> {
|
||||
return titleReplaceRules
|
||||
}
|
||||
@@ -85,9 +99,10 @@ class ContentProcessor private constructor(
|
||||
var sameTitleRemoved = false
|
||||
if (content != "null") {
|
||||
//去除重复标题
|
||||
if (enableRemoveSameTitle && BookHelp.removeSameTitle(book, chapter)) try {
|
||||
val fileName = chapter.getFileName("nr")
|
||||
if (!removeSameTitleCache.contains(fileName)) try {
|
||||
val name = Pattern.quote(book.name)
|
||||
var title = Pattern.quote(chapter.title)
|
||||
var title = chapter.title.escapeRegex().replace(spaceRegex, "\\\\s*")
|
||||
var matcher = Pattern.compile("^(\\s|\\p{P}|${name})*${title}(\\s)*")
|
||||
.matcher(mContent)
|
||||
if (matcher.find()) {
|
||||
|
||||
@@ -54,6 +54,7 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
|
||||
var preDownloadTask: Coroutine<*>? = null
|
||||
val downloadedChapters = hashSetOf<Int>()
|
||||
var contentProcessor: ContentProcessor? = null
|
||||
|
||||
//暂时保存跳转前进度
|
||||
fun saveCurrentBookProcess() {
|
||||
@@ -74,6 +75,7 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
readRecord.bookName = book.name
|
||||
readRecord.readTime = appDb.readRecordDao.getReadTime(book.name) ?: 0
|
||||
chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
|
||||
contentProcessor = ContentProcessor.get(book)
|
||||
durChapterIndex = book.durChapterIndex
|
||||
durChapterPos = book.durChapterPos
|
||||
isLocalBook = book.isLocal
|
||||
|
||||
@@ -11,6 +11,7 @@ import io.legado.app.base.VMBaseActivity
|
||||
import io.legado.app.constant.BookType
|
||||
import io.legado.app.data.entities.Book
|
||||
import io.legado.app.databinding.ActivityBookInfoEditBinding
|
||||
import io.legado.app.help.book.BookHelp
|
||||
import io.legado.app.help.book.isAudio
|
||||
import io.legado.app.help.book.isImage
|
||||
import io.legado.app.help.book.isLocal
|
||||
@@ -94,22 +95,23 @@ class BookInfoEditActivity :
|
||||
}
|
||||
|
||||
private fun saveData() = binding.run {
|
||||
viewModel.book?.let { book ->
|
||||
book.name = tieBookName.text?.toString() ?: ""
|
||||
book.author = tieBookAuthor.text?.toString() ?: ""
|
||||
val local = if (book.isLocal) BookType.local else 0
|
||||
book.type = when (spType.selectedItemPosition) {
|
||||
2 -> BookType.image or local
|
||||
1 -> BookType.audio or local
|
||||
else -> BookType.text or local
|
||||
}
|
||||
val customCoverUrl = tieCoverUrl.text?.toString()
|
||||
book.customCoverUrl = if (customCoverUrl == book.coverUrl) null else customCoverUrl
|
||||
book.customIntro = tieBookIntro.text?.toString()
|
||||
viewModel.saveBook(book) {
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
val book = viewModel.book ?: return@run
|
||||
val oldBook = book.copy()
|
||||
book.name = tieBookName.text?.toString() ?: ""
|
||||
book.author = tieBookAuthor.text?.toString() ?: ""
|
||||
val local = if (book.isLocal) BookType.local else 0
|
||||
book.type = when (spType.selectedItemPosition) {
|
||||
2 -> BookType.image or local
|
||||
1 -> BookType.audio or local
|
||||
else -> BookType.text or local
|
||||
}
|
||||
val customCoverUrl = tieCoverUrl.text?.toString()
|
||||
book.customCoverUrl = if (customCoverUrl == book.coverUrl) null else customCoverUrl
|
||||
book.customIntro = tieBookIntro.text?.toString()
|
||||
BookHelp.updateCacheFolder(oldBook, book)
|
||||
viewModel.saveBook(book) {
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,6 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
val searchResult = IntentData.get<SearchResult>("searchResult$key")
|
||||
val searchResultList = IntentData.get<List<SearchResult>>("searchResultList$key")
|
||||
if (searchResult != null && searchResultList != null) {
|
||||
ContentProcessor.enableRemoveSameTitle = false
|
||||
viewModel.searchContentQuery = searchResult.query
|
||||
binding.searchMenu.upSearchResultList(searchResultList)
|
||||
isShowingSearchResult = true
|
||||
@@ -294,6 +293,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
item.isVisible = BuildConfig.DEBUG
|
||||
item.isChecked = AppConfig.enableReview
|
||||
}
|
||||
|
||||
R.id.menu_reverse_content -> item.isVisible = onLine
|
||||
}
|
||||
}
|
||||
@@ -318,6 +318,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
showDialogFragment(ChangeBookSourceDialog(it.name, it.author))
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_chapter_change_source -> launch {
|
||||
val book = ReadBook.book ?: return@launch
|
||||
val chapter =
|
||||
@@ -328,6 +329,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
ChangeChapterSourceDialog(book.name, book.author, chapter.index, chapter.title)
|
||||
)
|
||||
}
|
||||
|
||||
R.id.menu_refresh,
|
||||
R.id.menu_refresh_dur -> {
|
||||
if (ReadBook.bookSource == null) {
|
||||
@@ -340,6 +342,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_refresh_after -> {
|
||||
if (ReadBook.bookSource == null) {
|
||||
upContent()
|
||||
@@ -351,6 +354,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_refresh_all -> {
|
||||
if (ReadBook.bookSource == null) {
|
||||
upContent()
|
||||
@@ -362,6 +366,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_download -> showDownloadDialog()
|
||||
R.id.menu_add_bookmark -> addBookmark()
|
||||
R.id.menu_edit_content -> showDialogFragment(ContentEditDialog())
|
||||
@@ -371,28 +376,34 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
loadChapterList(it)
|
||||
}
|
||||
|
||||
R.id.menu_enable_replace -> changeReplaceRuleState()
|
||||
R.id.menu_re_segment -> ReadBook.book?.let {
|
||||
it.setReSegment(!it.getReSegment())
|
||||
menu?.findItem(R.id.menu_re_segment)?.isChecked = it.getReSegment()
|
||||
ReadBook.loadContent(false)
|
||||
}
|
||||
|
||||
R.id.menu_enable_review -> {
|
||||
AppConfig.enableReview = !AppConfig.enableReview
|
||||
menu?.findItem(R.id.menu_enable_review)?.isChecked = AppConfig.enableReview
|
||||
ReadBook.loadContent(false)
|
||||
}
|
||||
|
||||
R.id.menu_page_anim -> showPageAnimConfig {
|
||||
binding.readView.upPageAnim()
|
||||
ReadBook.loadContent(false)
|
||||
}
|
||||
|
||||
R.id.menu_log -> showDialogFragment<AppLogDialog>()
|
||||
R.id.menu_toc_regex -> showDialogFragment(
|
||||
TxtTocRuleDialog(ReadBook.book?.tocUrl)
|
||||
)
|
||||
|
||||
R.id.menu_reverse_content -> ReadBook.book?.let {
|
||||
viewModel.reverseContent(it)
|
||||
}
|
||||
|
||||
R.id.menu_set_charset -> showCharsetConfig()
|
||||
R.id.menu_image_style -> {
|
||||
val imgStyles =
|
||||
@@ -405,12 +416,29 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
ReadBook.loadContent(false)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_get_progress -> ReadBook.book?.let {
|
||||
viewModel.syncBookProgress(it) { progress ->
|
||||
sureSyncProgress(progress)
|
||||
}
|
||||
}
|
||||
R.id.menu_same_title_removed -> viewModel.reverseRemoveSameTitle()
|
||||
|
||||
R.id.menu_same_title_removed -> {
|
||||
ReadBook.book?.let {
|
||||
val contentProcessor = ContentProcessor.get(it)
|
||||
val textChapter = ReadBook.curTextChapter
|
||||
if (textChapter != null
|
||||
&& !textChapter.sameTitleRemoved
|
||||
&& !contentProcessor.removeSameTitleCache.contains(
|
||||
textChapter.chapter.getFileName("nr")
|
||||
)
|
||||
) {
|
||||
toastOnUi("未找到可移除的重复标题")
|
||||
}
|
||||
}
|
||||
viewModel.reverseRemoveSameTitle()
|
||||
}
|
||||
|
||||
R.id.menu_help -> showReadMenuHelp()
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
@@ -477,34 +505,41 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
isNextKey(keyCode) -> {
|
||||
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
|
||||
binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||
if (volumeKeyPage(PageDirection.PREV)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||
if (volumeKeyPage(PageDirection.NEXT)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_PAGE_UP -> {
|
||||
binding.readView.pageDelegate?.keyTurnPage(PageDirection.PREV)
|
||||
return true
|
||||
}
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_PAGE_DOWN -> {
|
||||
binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT)
|
||||
return true
|
||||
}
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_SPACE -> {
|
||||
binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT)
|
||||
return true
|
||||
}
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_BACK -> {
|
||||
if (isShowingSearchResult) {
|
||||
exitSearchMenu()
|
||||
@@ -544,6 +579,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_BACK -> {
|
||||
event?.let {
|
||||
if ((event.flags and KeyEvent.FLAG_CANCELED_LONG_PRESS == 0)
|
||||
@@ -585,12 +621,14 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
event.rawX + cursorLeft.width,
|
||||
event.rawY - cursorLeft.height
|
||||
)
|
||||
|
||||
R.id.cursor_right -> readView.curPage.selectEndMove(
|
||||
event.rawX - cursorRight.width,
|
||||
event.rawY - cursorRight.height
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> showTextActionMenu()
|
||||
}
|
||||
return true
|
||||
@@ -657,6 +695,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
1 -> binding.readView.aloudStartSelect()
|
||||
else -> speak(binding.readView.getSelectText())
|
||||
}
|
||||
|
||||
R.id.menu_bookmark -> binding.readView.curPage.let {
|
||||
val bookmark = it.createBookmark()
|
||||
if (bookmark == null) {
|
||||
@@ -666,6 +705,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.menu_replace -> {
|
||||
val scopes = arrayListOf<String>()
|
||||
ReadBook.book?.name?.let {
|
||||
@@ -683,11 +723,13 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.menu_search_content -> {
|
||||
viewModel.searchContentQuery = selectedText
|
||||
openSearchActivity(selectedText)
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.menu_dict -> {
|
||||
showDialogFragment(DictDialog(selectedText))
|
||||
return true
|
||||
@@ -1010,7 +1052,6 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
override fun exitSearchMenu() {
|
||||
if (isShowingSearchResult) {
|
||||
isShowingSearchResult = false
|
||||
ContentProcessor.enableRemoveSameTitle = true
|
||||
binding.searchMenu.invalidate()
|
||||
binding.searchMenu.invisible()
|
||||
binding.readView.isTextSelected = false
|
||||
@@ -1110,6 +1151,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
ReadAloud.upReadAloudClass()
|
||||
ReadBook.readAloud()
|
||||
}
|
||||
|
||||
BaseReadAloudService.pause -> ReadAloud.resume(this)
|
||||
else -> ReadAloud.pause(this)
|
||||
}
|
||||
@@ -1142,6 +1184,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
viewModel.saveImage(src, Uri.parse(path))
|
||||
}
|
||||
}
|
||||
|
||||
"selectFolder" -> selectImageDir.launch()
|
||||
}
|
||||
popupAction.dismiss()
|
||||
@@ -1164,15 +1207,18 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
setCurTextColor(color)
|
||||
postEvent(EventBus.UP_CONFIG, false)
|
||||
}
|
||||
|
||||
BG_COLOR -> {
|
||||
setCurBg(0, "#${color.hexString}")
|
||||
postEvent(EventBus.UP_CONFIG, false)
|
||||
}
|
||||
|
||||
TIP_COLOR -> {
|
||||
ReadTipConfig.tipColor = color
|
||||
postEvent(EventBus.TIP_COLOR, "")
|
||||
postEvent(EventBus.UP_CONFIG, true)
|
||||
}
|
||||
|
||||
TIP_DIVIDER_COLOR -> {
|
||||
ReadTipConfig.tipDividerColor = color
|
||||
postEvent(EventBus.TIP_COLOR, "")
|
||||
@@ -1233,6 +1279,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
lineIndex,
|
||||
charIndex + viewModel.searchContentQuery.length - 1
|
||||
)
|
||||
|
||||
1 -> binding.readView.curPage.selectEndMoveIndex(
|
||||
0, lineIndex + 1, charIndex2
|
||||
)
|
||||
|
||||
@@ -155,6 +155,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
is SecurityException, is FileNotFoundException -> {
|
||||
permissionDenialLiveData.postValue(1)
|
||||
}
|
||||
|
||||
else -> {
|
||||
AppLog.put("LoadTocError:${it.localizedMessage}", it)
|
||||
ReadBook.upMsg("LoadTocError:${it.localizedMessage}")
|
||||
@@ -434,14 +435,12 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
*/
|
||||
fun reverseRemoveSameTitle() {
|
||||
execute {
|
||||
val book = ReadBook.book
|
||||
val textChapter = ReadBook.curTextChapter
|
||||
if (book != null && textChapter != null) {
|
||||
BookHelp.setRemoveSameTitle(
|
||||
book, textChapter.chapter, !textChapter.sameTitleRemoved
|
||||
)
|
||||
ReadBook.loadContent(ReadBook.durChapterIndex)
|
||||
}
|
||||
val book = ReadBook.book ?: return@execute
|
||||
val textChapter = ReadBook.curTextChapter ?: return@execute
|
||||
BookHelp.setRemoveSameTitle(
|
||||
book, textChapter.chapter, !textChapter.sameTitleRemoved
|
||||
)
|
||||
ReadBook.loadContent(ReadBook.durChapterIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,10 @@ import io.legado.app.ui.widget.recycler.UpLinearLayoutManager
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -47,6 +49,7 @@ class SearchContentActivity :
|
||||
}
|
||||
private var durChapterIndex = 0
|
||||
private var searchJob: Job? = null
|
||||
private var initJob: Deferred<*>? = null
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
val bbg = bottomBackground
|
||||
@@ -56,38 +59,37 @@ class SearchContentActivity :
|
||||
binding.ivSearchContentTop.setColorFilter(btc)
|
||||
binding.ivSearchContentBottom.setColorFilter(btc)
|
||||
val searchResultList = IntentData.get<List<SearchResult>>("searchResultList")
|
||||
val position = intent.getIntExtra("searchResultIndex", 0)
|
||||
val noSearchResult = searchResultList == null
|
||||
initSearchView(!noSearchResult)
|
||||
initRecyclerView()
|
||||
initView()
|
||||
intent.getStringExtra("bookUrl")?.let { bookUrl ->
|
||||
viewModel.initBook(bookUrl) {
|
||||
searchResultList?.let {
|
||||
viewModel.searchResultList.addAll(it)
|
||||
viewModel.searchResultCounts = it.size
|
||||
adapter.setItems(it)
|
||||
val position = intent.getIntExtra("searchResultIndex", 0)
|
||||
binding.recyclerView.scrollToPosition(position)
|
||||
}
|
||||
initBook(noSearchResult)
|
||||
}
|
||||
val bookUrl = intent.getStringExtra("bookUrl") ?: return
|
||||
viewModel.initBook(bookUrl) {
|
||||
initSearchResultList(searchResultList, position)
|
||||
initBook(noSearchResult)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||
if (ev.action == MotionEvent.ACTION_DOWN) {
|
||||
searchView.post {
|
||||
currentFocus?.let {
|
||||
if (it is EditText) {
|
||||
it.clearFocus()
|
||||
it.hideSoftInput()
|
||||
}
|
||||
currentFocus?.let {
|
||||
if (it.shouldHideSoftInput(ev)) {
|
||||
it.hideSoftInput()
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
private fun initSearchResultList(list: List<SearchResult>?, position: Int) {
|
||||
list ?: return
|
||||
viewModel.searchResultList.addAll(list)
|
||||
viewModel.searchResultCounts = list.size
|
||||
adapter.setItems(list)
|
||||
binding.recyclerView.scrollToPosition(position)
|
||||
}
|
||||
|
||||
private fun initSearchView(clearFocus: Boolean) {
|
||||
searchView.applyTint(primaryTextColor)
|
||||
searchView.onActionViewExpanded()
|
||||
@@ -149,7 +151,7 @@ class SearchContentActivity :
|
||||
}
|
||||
|
||||
private fun initCacheFileNames(book: Book) {
|
||||
launch {
|
||||
initJob = async {
|
||||
withContext(IO) {
|
||||
viewModel.cacheChapterNames.addAll(BookHelp.getChapterFiles(book))
|
||||
}
|
||||
@@ -179,8 +181,8 @@ class SearchContentActivity :
|
||||
viewModel.lastQuery = query
|
||||
binding.refreshProgressBar.isAutoLoading = true
|
||||
binding.fbStop.visible()
|
||||
ContentProcessor.enableRemoveSameTitle = false
|
||||
searchJob = launch(IO) {
|
||||
initJob?.await()
|
||||
kotlin.runCatching {
|
||||
appDb.bookChapterDao.getChapterList(viewModel.bookUrl).forEach { bookChapter ->
|
||||
ensureActive()
|
||||
@@ -211,7 +213,6 @@ class SearchContentActivity :
|
||||
}.onFailure {
|
||||
AppLog.put("全文搜索出错\n${it.localizedMessage}", it)
|
||||
}
|
||||
ContentProcessor.enableRemoveSameTitle = true
|
||||
binding.tvCurrentSearchInfo.post {
|
||||
binding.fbStop.invisible()
|
||||
binding.refreshProgressBar.isAutoLoading = false
|
||||
|
||||
@@ -18,7 +18,7 @@ import kotlin.coroutines.coroutineContext
|
||||
class SearchContentViewModel(application: Application) : BaseViewModel(application) {
|
||||
var bookUrl: String = ""
|
||||
var book: Book? = null
|
||||
private var contentProcessor: ContentProcessor? = null
|
||||
var contentProcessor: ContentProcessor? = null
|
||||
var lastQuery: String = ""
|
||||
var searchResultCounts = 0
|
||||
val cacheChapterNames = hashSetOf<String>()
|
||||
@@ -38,10 +38,9 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
|
||||
|
||||
suspend fun searchChapter(
|
||||
query: String,
|
||||
chapter: BookChapter?
|
||||
chapter: BookChapter
|
||||
): List<SearchResult> {
|
||||
val searchResultsWithinChapter: MutableList<SearchResult> = mutableListOf()
|
||||
chapter ?: return searchResultsWithinChapter
|
||||
val book = book ?: return searchResultsWithinChapter
|
||||
val chapterContent = BookHelp.getContent(book, chapter) ?: return searchResultsWithinChapter
|
||||
coroutineContext.ensureActive()
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.icu.text.Collator
|
||||
import android.icu.util.ULocale
|
||||
import android.net.Uri
|
||||
import android.text.Editable
|
||||
import io.legado.app.constant.AppPattern
|
||||
import io.legado.app.constant.AppPattern.dataUriRegex
|
||||
import java.io.File
|
||||
import java.lang.Character.codePointCount
|
||||
@@ -127,3 +128,7 @@ fun CharSequence.toStringArray(): Array<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fun String.escapeRegex(): String {
|
||||
return replace(AppPattern.regexCharRegex, "\\\\$0")
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.os.Build
|
||||
import android.text.Html
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.*
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
@@ -195,4 +196,17 @@ fun PopupMenu.show(x: Int, y: Int) {
|
||||
}.onFailure {
|
||||
it.printOnDebug()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun View.shouldHideSoftInput(event: MotionEvent): Boolean {
|
||||
if (this is EditText) {
|
||||
val l = intArrayOf(0, 0)
|
||||
getLocationInWindow(l)
|
||||
val left = l[0]
|
||||
val top = l[1]
|
||||
val bottom = top + getHeight()
|
||||
val right = left + getWidth()
|
||||
return !(event.x > left && event.x < right && event.y > top && event.y < bottom)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user