Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -31,6 +31,16 @@ interface BookSourceDao {
|
|||||||
)
|
)
|
||||||
fun flowSearch(searchKey: String): Flow<List<BookSourcePart>>
|
fun flowSearch(searchKey: String): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""select * from book_sources
|
||||||
|
where bookSourceName like '%' || :searchKey || '%'
|
||||||
|
or bookSourceGroup like '%' || :searchKey || '%'
|
||||||
|
or bookSourceUrl like '%' || :searchKey || '%'
|
||||||
|
or bookSourceComment like '%' || :searchKey || '%'
|
||||||
|
order by customOrder asc"""
|
||||||
|
)
|
||||||
|
fun search(searchKey: String): List<BookSource>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
@@ -56,27 +66,45 @@ interface BookSourceDao {
|
|||||||
)
|
)
|
||||||
fun flowGroupSearch(searchKey: String): Flow<List<BookSourcePart>>
|
fun flowGroupSearch(searchKey: String): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
@Query(
|
||||||
|
"""select * from book_sources
|
||||||
|
where bookSourceGroup = :searchKey
|
||||||
|
or bookSourceGroup like :searchKey || ',%'
|
||||||
|
or bookSourceGroup like '%,' || :searchKey
|
||||||
|
or bookSourceGroup like '%,' || :searchKey || ',%'
|
||||||
|
order by customOrder asc"""
|
||||||
|
)
|
||||||
|
fun groupSearch(searchKey: String): List<BookSource>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
from book_sources where enabled = 1 order by customOrder asc""")
|
from book_sources where enabled = 1 order by customOrder asc"""
|
||||||
|
)
|
||||||
fun flowEnabled(): Flow<List<BookSourcePart>>
|
fun flowEnabled(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
@Query(
|
||||||
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
from book_sources where enabled = 0 order by customOrder asc""")
|
from book_sources where enabled = 0 order by customOrder asc"""
|
||||||
|
)
|
||||||
fun flowDisabled(): Flow<List<BookSourcePart>>
|
fun flowDisabled(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc")
|
@Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc")
|
||||||
fun flowExplore(): Flow<List<BookSource>>
|
fun flowExplore(): Flow<List<BookSource>>
|
||||||
|
|
||||||
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
@Query(
|
||||||
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
from book_sources where loginUrl is not null and loginUrl != ''""")
|
from book_sources where loginUrl is not null and loginUrl != ''"""
|
||||||
|
)
|
||||||
fun flowLogin(): Flow<List<BookSourcePart>>
|
fun flowLogin(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
@Query(
|
||||||
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
from book_sources where bookSourceGroup is null or bookSourceGroup = '' or bookSourceGroup like '%未分组%'""")
|
from book_sources where bookSourceGroup is null or bookSourceGroup = '' or bookSourceGroup like '%未分组%'"""
|
||||||
|
)
|
||||||
fun flowNoGroup(): Flow<List<BookSourcePart>>
|
fun flowNoGroup(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
@@ -147,6 +175,15 @@ interface BookSourceDao {
|
|||||||
@get:Query("select * from book_sources where enabled = 1 order by customOrder")
|
@get:Query("select * from book_sources where enabled = 1 order by customOrder")
|
||||||
val allEnabled: List<BookSource>
|
val allEnabled: List<BookSource>
|
||||||
|
|
||||||
|
@get:Query("select * from book_sources where enabled = 0 order by customOrder")
|
||||||
|
val allDisabled: List<BookSource>
|
||||||
|
|
||||||
|
@get:Query("select * from book_sources where bookSourceGroup is null or bookSourceGroup = '' or bookSourceGroup like '%未分组%'")
|
||||||
|
val allNoGroup: List<BookSource>
|
||||||
|
|
||||||
|
@get:Query("select * from book_sources where loginUrl is not null and loginUrl != ''")
|
||||||
|
val allLogin: List<BookSource>
|
||||||
|
|
||||||
@get:Query("select * from book_sources where enabled = 1 and bookSourceType = 0 order by customOrder")
|
@get:Query("select * from book_sources where enabled = 1 and bookSourceType = 0 order by customOrder")
|
||||||
val allTextEnabled: List<BookSource>
|
val allTextEnabled: List<BookSource>
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class ContentProcessor private constructor(
|
|||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
mContent = mContent.substring(matcher.end())
|
mContent = mContent.substring(matcher.end())
|
||||||
sameTitleRemoved = true
|
sameTitleRemoved = true
|
||||||
} else if (useReplace) {
|
} else if (useReplace && book.getUseReplaceRule()) {
|
||||||
title = Pattern.quote(
|
title = Pattern.quote(
|
||||||
chapter.getDisplayTitle(
|
chapter.getDisplayTitle(
|
||||||
contentReplaceRules,
|
contentReplaceRules,
|
||||||
|
|||||||
@@ -174,12 +174,13 @@ open class WebDav(
|
|||||||
val baseUrl = NetworkUtils.getBaseUrl(urlStr)
|
val baseUrl = NetworkUtils.getBaseUrl(urlStr)
|
||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
//依然是优化支持 caddy 自建的 WebDav ,其目录后缀都为“/”, 所以删除“/”的判定,不然无法获取该目录项
|
//依然是优化支持 caddy 自建的 WebDav ,其目录后缀都为“/”, 所以删除“/”的判定,不然无法获取该目录项
|
||||||
val href = URLDecoder.decode(element.findNS("href", ns)[0].text(), "UTF-8")
|
val href = element.findNS("href", ns)[0].text().replace("+", "%20")
|
||||||
|
val hrefDecode = URLDecoder.decode(href, "UTF-8")
|
||||||
.removeSuffix("/")
|
.removeSuffix("/")
|
||||||
val fileName = href.substringAfterLast("/")
|
val fileName = hrefDecode.substringAfterLast("/")
|
||||||
val webDavFile: WebDav
|
val webDavFile: WebDav
|
||||||
try {
|
try {
|
||||||
val urlName = href.ifEmpty {
|
val urlName = hrefDecode.ifEmpty {
|
||||||
url.file.replace("/", "")
|
url.file.replace("/", "")
|
||||||
}
|
}
|
||||||
val contentType = element
|
val contentType = element
|
||||||
@@ -199,7 +200,7 @@ open class WebDav(
|
|||||||
.toInstant(ZoneOffset.of("+8")).toEpochMilli()
|
.toInstant(ZoneOffset.of("+8")).toEpochMilli()
|
||||||
}
|
}
|
||||||
}.getOrNull() ?: 0
|
}.getOrNull() ?: 0
|
||||||
val fullURL = NetworkUtils.getAbsoluteURL(baseUrl, href)
|
val fullURL = NetworkUtils.getAbsoluteURL(baseUrl, hrefDecode)
|
||||||
webDavFile = WebDavFile(
|
webDavFile = WebDavFile(
|
||||||
fullURL,
|
fullURL,
|
||||||
authorization,
|
authorization,
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ class WebDavFile(
|
|||||||
val contentType: String,
|
val contentType: String,
|
||||||
val resourceType: String,
|
val resourceType: String,
|
||||||
val lastModify: Long
|
val lastModify: Long
|
||||||
) : WebDav(urlStr, authorization) {
|
) : WebDav(urlStr, authorization) {
|
||||||
|
|
||||||
val isDir by lazy {
|
val isDir by lazy {
|
||||||
contentType == "httpd/unix-directory" || resourceType.lowercase().contains("collection")
|
contentType == "httpd/unix-directory" || resourceType.lowercase().contains("collection")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ class AnalyzeRule(
|
|||||||
}
|
}
|
||||||
if (result == null) result = ""
|
if (result == null) result = ""
|
||||||
val str = if (unescape) {
|
val str = if (unescape) {
|
||||||
StringEscapeUtils.unescapeHtml3(result.toString())
|
StringEscapeUtils.unescapeHtml4(result.toString())
|
||||||
} else result.toString()
|
} else result.toString()
|
||||||
if (isUrl) {
|
if (isUrl) {
|
||||||
return if (str.isBlank()) {
|
return if (str.isBlank()) {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ object LocalBook {
|
|||||||
}
|
}
|
||||||
if (book.isEpub) {
|
if (book.isEpub) {
|
||||||
content = content?.replace("<img", "< img", true) ?: return null
|
content = content?.replace("<img", "< img", true) ?: return null
|
||||||
return StringEscapeUtils.unescapeHtml3(content)
|
return StringEscapeUtils.unescapeHtml4(content)
|
||||||
}
|
}
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ class RemoteBookWebDav(
|
|||||||
val remoteWebDavFileList: List<WebDavFile> = WebDav(path, authorization).listFiles()
|
val remoteWebDavFileList: List<WebDavFile> = WebDav(path, authorization).listFiles()
|
||||||
//转化远程文件信息到本地对象
|
//转化远程文件信息到本地对象
|
||||||
remoteWebDavFileList.forEach { webDavFile ->
|
remoteWebDavFileList.forEach { webDavFile ->
|
||||||
if (webDavFile.isDir || bookFileRegex.matches(webDavFile.displayName) || archiveFileRegex.matches(webDavFile.displayName)
|
if (webDavFile.isDir
|
||||||
|
|| bookFileRegex.matches(webDavFile.displayName)
|
||||||
|
|| archiveFileRegex.matches(webDavFile.displayName)
|
||||||
) {
|
) {
|
||||||
//扩展名符合阅读的格式则认为是书籍
|
//扩展名符合阅读的格式则认为是书籍
|
||||||
remoteBooks.add(RemoteBook(webDavFile))
|
remoteBooks.add(RemoteBook(webDavFile))
|
||||||
@@ -70,7 +72,7 @@ class RemoteBookWebDav(
|
|||||||
if (!NetworkUtils.isAvailable()) throw NoStackTraceException("网络不可用")
|
if (!NetworkUtils.isAvailable()) throw NoStackTraceException("网络不可用")
|
||||||
val localBookUri = Uri.parse(book.bookUrl)
|
val localBookUri = Uri.parse(book.bookUrl)
|
||||||
val putUrl = "$rootBookUrl${File.separator}${book.originName}"
|
val putUrl = "$rootBookUrl${File.separator}${book.originName}"
|
||||||
val webDav = WebDav(putUrl, authorization)
|
val webDav = WebDav(putUrl, authorization)
|
||||||
if (localBookUri.isContentScheme()) {
|
if (localBookUri.isContentScheme()) {
|
||||||
webDav.upload(
|
webDav.upload(
|
||||||
byteArray = localBookUri.readBytes(appCtx),
|
byteArray = localBookUri.readBytes(appCtx),
|
||||||
@@ -80,8 +82,8 @@ class RemoteBookWebDav(
|
|||||||
webDav.upload(localBookUri.path!!)
|
webDav.upload(localBookUri.path!!)
|
||||||
}
|
}
|
||||||
book.origin = BookType.webDavTag + CustomUrl(putUrl)
|
book.origin = BookType.webDavTag + CustomUrl(putUrl)
|
||||||
.putAttribute("serverID", serverID)
|
.putAttribute("serverID", serverID)
|
||||||
.toString()
|
.toString()
|
||||||
book.save()
|
book.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import kotlinx.coroutines.Dispatchers.IO
|
|||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.apache.commons.text.StringEscapeUtils
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@@ -168,6 +169,7 @@ object BookContent {
|
|||||||
//获取正文
|
//获取正文
|
||||||
var content = analyzeRule.getString(contentRule.content, unescape = false)
|
var content = analyzeRule.getString(contentRule.content, unescape = false)
|
||||||
content = HtmlFormatter.formatKeepImg(content, rUrl)
|
content = HtmlFormatter.formatKeepImg(content, rUrl)
|
||||||
|
content = StringEscapeUtils.unescapeHtml4(content)
|
||||||
//获取下一页链接
|
//获取下一页链接
|
||||||
if (getNextPageUrl) {
|
if (getNextPageUrl) {
|
||||||
val nextUrlRule = contentRule.nextContentUrl
|
val nextUrlRule = contentRule.nextContentUrl
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import io.legado.app.constant.AppLog
|
|||||||
import io.legado.app.constant.EventBus
|
import io.legado.app.constant.EventBus
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.BookSourcePart
|
import io.legado.app.data.entities.BookSourcePart
|
||||||
import io.legado.app.data.entities.toBookSource
|
|
||||||
import io.legado.app.databinding.ActivityBookSourceBinding
|
import io.legado.app.databinding.ActivityBookSourceBinding
|
||||||
import io.legado.app.databinding.DialogEditTextBinding
|
import io.legado.app.databinding.DialogEditTextBinding
|
||||||
import io.legado.app.help.DirectLinkUpload
|
import io.legado.app.help.DirectLinkUpload
|
||||||
@@ -71,6 +70,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
private var sortAscending = true
|
private var sortAscending = true
|
||||||
private var snackBar: Snackbar? = null
|
private var snackBar: Snackbar? = null
|
||||||
private var isPaused = false
|
private var isPaused = false
|
||||||
|
private var searchKey: String? = null
|
||||||
private val qrResult = registerForActivityResult(QrCodeResult()) {
|
private val qrResult = registerForActivityResult(QrCodeResult()) {
|
||||||
it ?: return@registerForActivityResult
|
it ?: return@registerForActivityResult
|
||||||
showDialogFragment(ImportBookSourceDialog(it))
|
showDialogFragment(ImportBookSourceDialog(it))
|
||||||
@@ -235,6 +235,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
|
|
||||||
|
|
||||||
private fun upBookSource(searchKey: String? = null) {
|
private fun upBookSource(searchKey: String? = null) {
|
||||||
|
this.searchKey = searchKey
|
||||||
sourceFlowJob?.cancel()
|
sourceFlowJob?.cancel()
|
||||||
sourceFlowJob = launch {
|
sourceFlowJob = launch {
|
||||||
when {
|
when {
|
||||||
@@ -376,7 +377,12 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
R.id.menu_bottom_sel -> viewModel.bottomSource(*adapter.selection.toTypedArray())
|
R.id.menu_bottom_sel -> viewModel.bottomSource(*adapter.selection.toTypedArray())
|
||||||
R.id.menu_add_group -> selectionAddToGroups()
|
R.id.menu_add_group -> selectionAddToGroups()
|
||||||
R.id.menu_remove_group -> selectionRemoveFromGroups()
|
R.id.menu_remove_group -> selectionRemoveFromGroups()
|
||||||
R.id.menu_export_selection -> viewModel.saveToFile(adapter.selection.toBookSource()) { file ->
|
R.id.menu_export_selection -> viewModel.saveToFile(
|
||||||
|
adapter,
|
||||||
|
searchKey,
|
||||||
|
sortAscending,
|
||||||
|
sort
|
||||||
|
) { file ->
|
||||||
exportDir.launch {
|
exportDir.launch {
|
||||||
mode = HandleFileContract.EXPORT
|
mode = HandleFileContract.EXPORT
|
||||||
fileData = HandleFileContract.FileData(
|
fileData = HandleFileContract.FileData(
|
||||||
@@ -387,7 +393,12 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_share_source -> viewModel.saveToFile(adapter.selection.toBookSource()) {
|
R.id.menu_share_source -> viewModel.saveToFile(
|
||||||
|
adapter,
|
||||||
|
searchKey,
|
||||||
|
sortAscending,
|
||||||
|
sort
|
||||||
|
) {
|
||||||
share(it)
|
share(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,19 @@ package io.legado.app.ui.book.source.manage
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseViewModel
|
import io.legado.app.base.BaseViewModel
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.BookSource
|
import io.legado.app.data.entities.BookSource
|
||||||
import io.legado.app.data.entities.BookSourcePart
|
import io.legado.app.data.entities.BookSourcePart
|
||||||
|
import io.legado.app.data.entities.toBookSource
|
||||||
import io.legado.app.help.config.SourceConfig
|
import io.legado.app.help.config.SourceConfig
|
||||||
import io.legado.app.utils.*
|
import io.legado.app.utils.*
|
||||||
|
import splitties.init.appCtx
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import io.legado.app.ui.book.source.manage.BookSourceActivity.Sort
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 书源管理数据修改
|
* 书源管理数据修改
|
||||||
@@ -56,10 +61,10 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
|
|||||||
if (items.isEmpty()) return
|
if (items.isEmpty()) return
|
||||||
execute {
|
execute {
|
||||||
val firstSortNumber = items[0].customOrder
|
val firstSortNumber = items[0].customOrder
|
||||||
items.forEachIndexed { index, bookSource ->
|
val array = items.mapIndexed { index, bookSource ->
|
||||||
bookSource.customOrder = firstSortNumber + index
|
bookSource.copy(customOrder = firstSortNumber + index)
|
||||||
}
|
}
|
||||||
appDb.bookSourceDao.upOrder(items)
|
appDb.bookSourceDao.upOrder(array)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,13 +127,15 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
fun saveToFile(sources: List<BookSource>, success: (file: File) -> Unit) {
|
private fun saveToFile(sources: List<BookSource>, success: (file: File) -> Unit) {
|
||||||
execute {
|
execute {
|
||||||
val path = "${context.filesDir}/shareBookSource.json"
|
val path = "${context.filesDir}/shareBookSource.json"
|
||||||
FileUtils.delete(path)
|
FileUtils.delete(path)
|
||||||
val file = FileUtils.createFileWithReplace(path)
|
val file = FileUtils.createFileWithReplace(path)
|
||||||
FileOutputStream(file).use {
|
FileOutputStream(file).use { out ->
|
||||||
GSON.writeToOutputStream(it, sources)
|
BufferedOutputStream(out, 64 * 1024).use {
|
||||||
|
GSON.writeToOutputStream(it, sources)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
file
|
file
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
@@ -138,6 +145,107 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveToFile(
|
||||||
|
adapter: BookSourceAdapter,
|
||||||
|
searchKey: String?,
|
||||||
|
sortAscending: Boolean,
|
||||||
|
sort: Sort,
|
||||||
|
success: (file: File) -> Unit
|
||||||
|
) {
|
||||||
|
execute {
|
||||||
|
val selection = adapter.selection
|
||||||
|
val selectedRate = selection.size.toFloat() / adapter.itemCount.toFloat()
|
||||||
|
val sources = if (selectedRate == 1f) {
|
||||||
|
getBookSources(searchKey, sortAscending, sort)
|
||||||
|
} else if (selectedRate < 0.3) {
|
||||||
|
selection.toBookSource()
|
||||||
|
} else {
|
||||||
|
val keys = selection.map { it.bookSourceUrl }.toHashSet()
|
||||||
|
val bookSources = getBookSources(searchKey, sortAscending, sort)
|
||||||
|
bookSources.filter {
|
||||||
|
keys.contains(it.bookSourceUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveToFile(sources, success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBookSources(
|
||||||
|
searchKey: String?,
|
||||||
|
sortAscending: Boolean,
|
||||||
|
sort: Sort
|
||||||
|
): List<BookSource> {
|
||||||
|
return when {
|
||||||
|
searchKey.isNullOrEmpty() -> {
|
||||||
|
appDb.bookSourceDao.all
|
||||||
|
}
|
||||||
|
|
||||||
|
searchKey == appCtx.getString(R.string.enabled) -> {
|
||||||
|
appDb.bookSourceDao.allEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
searchKey == appCtx.getString(R.string.disabled) -> {
|
||||||
|
appDb.bookSourceDao.allDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
searchKey == appCtx.getString(R.string.need_login) -> {
|
||||||
|
appDb.bookSourceDao.allLogin
|
||||||
|
}
|
||||||
|
|
||||||
|
searchKey == appCtx.getString(R.string.no_group) -> {
|
||||||
|
appDb.bookSourceDao.allNoGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
searchKey.startsWith("group:") -> {
|
||||||
|
val key = searchKey.substringAfter("group:")
|
||||||
|
appDb.bookSourceDao.groupSearch(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
appDb.bookSourceDao.search(searchKey)
|
||||||
|
}
|
||||||
|
}.let { data ->
|
||||||
|
if (sortAscending) when (sort) {
|
||||||
|
Sort.Weight -> data.sortedBy { it.weight }
|
||||||
|
Sort.Name -> data.sortedWith { o1, o2 ->
|
||||||
|
o1.bookSourceName.cnCompare(o2.bookSourceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
Sort.Url -> data.sortedBy { it.bookSourceUrl }
|
||||||
|
Sort.Update -> data.sortedByDescending { it.lastUpdateTime }
|
||||||
|
Sort.Respond -> data.sortedBy { it.respondTime }
|
||||||
|
Sort.Enable -> data.sortedWith { o1, o2 ->
|
||||||
|
var sortNum = -o1.enabled.compareTo(o2.enabled)
|
||||||
|
if (sortNum == 0) {
|
||||||
|
sortNum = o1.bookSourceName.cnCompare(o2.bookSourceName)
|
||||||
|
}
|
||||||
|
sortNum
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> data
|
||||||
|
}
|
||||||
|
else when (sort) {
|
||||||
|
Sort.Weight -> data.sortedByDescending { it.weight }
|
||||||
|
Sort.Name -> data.sortedWith { o1, o2 ->
|
||||||
|
o2.bookSourceName.cnCompare(o1.bookSourceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
Sort.Url -> data.sortedByDescending { it.bookSourceUrl }
|
||||||
|
Sort.Update -> data.sortedBy { it.lastUpdateTime }
|
||||||
|
Sort.Respond -> data.sortedByDescending { it.respondTime }
|
||||||
|
Sort.Enable -> data.sortedWith { o1, o2 ->
|
||||||
|
var sortNum = o1.enabled.compareTo(o2.enabled)
|
||||||
|
if (sortNum == 0) {
|
||||||
|
sortNum = o1.bookSourceName.cnCompare(o2.bookSourceName)
|
||||||
|
}
|
||||||
|
sortNum
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> data.reversed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addGroup(group: String) {
|
fun addGroup(group: String) {
|
||||||
execute {
|
execute {
|
||||||
val sources = appDb.bookSourceDao.noGroup
|
val sources = appDb.bookSourceDao.noGroup
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ object HtmlFormatter {
|
|||||||
.replace("\\s*\\n+\\s*".toRegex(), "\n ")
|
.replace("\\s*\\n+\\s*".toRegex(), "\n ")
|
||||||
.replace("^[\\n\\s]+".toRegex(), " ")
|
.replace("^[\\n\\s]+".toRegex(), " ")
|
||||||
.replace("[\\n\\s]+$".toRegex(), "")
|
.replace("[\\n\\s]+$".toRegex(), "")
|
||||||
.let {
|
|
||||||
StringEscapeUtils.unescapeHtml3(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatKeepImg(html: String?, redirectUrl: URL? = null): String {
|
fun formatKeepImg(html: String?, redirectUrl: URL? = null): String {
|
||||||
|
|||||||
@@ -9,16 +9,12 @@ import java.io.InputStream
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object MD5Utils {
|
object MD5Utils {
|
||||||
|
|
||||||
private val MD5Digester by lazy {
|
|
||||||
DigestUtil.digester("MD5")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun md5Encode(str: String?): String {
|
fun md5Encode(str: String?): String {
|
||||||
return MD5Digester.digestHex(str)
|
return DigestUtil.digester("MD5").digestHex(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun md5Encode(inputStream: InputStream): String {
|
fun md5Encode(inputStream: InputStream): String {
|
||||||
return MD5Digester.digestHex(inputStream)
|
return DigestUtil.digester("MD5").digestHex(inputStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun md5Encode16(str: String): String {
|
fun md5Encode16(str: String): String {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.legado.app.utils
|
package io.legado.app.utils
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -21,6 +22,7 @@ object NetworkUtils {
|
|||||||
/**
|
/**
|
||||||
* 判断是否联网
|
* 判断是否联网
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun isAvailable(): Boolean {
|
fun isAvailable(): Boolean {
|
||||||
if (Build.VERSION.SDK_INT < 23) {
|
if (Build.VERSION.SDK_INT < 23) {
|
||||||
@@ -31,7 +33,9 @@ object NetworkUtils {
|
|||||||
// 移动数据
|
// 移动数据
|
||||||
mWiFiNetworkInfo.type == ConnectivityManager.TYPE_MOBILE ||
|
mWiFiNetworkInfo.type == ConnectivityManager.TYPE_MOBILE ||
|
||||||
// 以太网
|
// 以太网
|
||||||
mWiFiNetworkInfo.type == ConnectivityManager.TYPE_ETHERNET
|
mWiFiNetworkInfo.type == ConnectivityManager.TYPE_ETHERNET ||
|
||||||
|
// VPN
|
||||||
|
mWiFiNetworkInfo.type == ConnectivityManager.TYPE_VPN
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val network = connectivityManager.activeNetwork
|
val network = connectivityManager.activeNetwork
|
||||||
@@ -43,7 +47,9 @@ object NetworkUtils {
|
|||||||
// 移动数据
|
// 移动数据
|
||||||
nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
|
nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
|
||||||
// 以太网
|
// 以太网
|
||||||
nc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
|
nc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) ||
|
||||||
|
// VPN
|
||||||
|
nc.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user