This commit is contained in:
Horis
2023-06-07 23:52:59 +08:00
parent 271b139073
commit 45c88716f3
15 changed files with 195 additions and 65 deletions
+5
View File
@@ -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
}