mirror of
https://github.com/gedoor/legado.git
synced 2024-08-30 09:23:26 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
ba90882998
@ -5,6 +5,7 @@ import android.content.Context
|
|||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.os.postDelayed
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@ -160,6 +161,12 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
|
|||||||
onCurrentListChanged()
|
onCurrentListChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
handler.postDelayed(1000) {
|
||||||
|
if (diffJob?.isCompleted == false) {
|
||||||
|
diffJob?.cancel()
|
||||||
|
setItems(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +130,7 @@ object PreferKey {
|
|||||||
const val clearWebViewData = "clearWebViewData"
|
const val clearWebViewData = "clearWebViewData"
|
||||||
const val onlyLatestBackup = "onlyLatestBackup"
|
const val onlyLatestBackup = "onlyLatestBackup"
|
||||||
const val brightnessVwPos = "brightnessVwPos"
|
const val brightnessVwPos = "brightnessVwPos"
|
||||||
|
const val shrinkDatabase = "shrinkDatabase"
|
||||||
|
|
||||||
const val cPrimary = "colorPrimary"
|
const val cPrimary = "colorPrimary"
|
||||||
const val cAccent = "colorAccent"
|
const val cAccent = "colorAccent"
|
||||||
|
@ -3,6 +3,7 @@ package io.legado.app.data.dao
|
|||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import io.legado.app.constant.AppPattern
|
import io.legado.app.constant.AppPattern
|
||||||
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.utils.cnCompare
|
import io.legado.app.utils.cnCompare
|
||||||
import io.legado.app.utils.splitNotBlank
|
import io.legado.app.utils.splitNotBlank
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -11,21 +12,29 @@ import kotlinx.coroutines.flow.map
|
|||||||
@Dao
|
@Dao
|
||||||
interface BookSourceDao {
|
interface BookSourceDao {
|
||||||
|
|
||||||
@Query("select * from book_sources order by customOrder asc")
|
@Query(
|
||||||
fun flowAll(): Flow<List<BookSource>>
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources order by customOrder asc"""
|
||||||
|
)
|
||||||
|
fun flowAll(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""select * from book_sources
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources
|
||||||
where bookSourceName like '%' || :searchKey || '%'
|
where bookSourceName like '%' || :searchKey || '%'
|
||||||
or bookSourceGroup like '%' || :searchKey || '%'
|
or bookSourceGroup like '%' || :searchKey || '%'
|
||||||
or bookSourceUrl like '%' || :searchKey || '%'
|
or bookSourceUrl like '%' || :searchKey || '%'
|
||||||
or bookSourceComment like '%' || :searchKey || '%'
|
or bookSourceComment like '%' || :searchKey || '%'
|
||||||
order by customOrder asc"""
|
order by customOrder asc"""
|
||||||
)
|
)
|
||||||
fun flowSearch(searchKey: String): Flow<List<BookSource>>
|
fun flowSearch(searchKey: String): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""select * from book_sources
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources
|
||||||
where enabled = 1 and
|
where enabled = 1 and
|
||||||
(bookSourceName like '%' || :searchKey || '%'
|
(bookSourceName like '%' || :searchKey || '%'
|
||||||
or bookSourceGroup like '%' || :searchKey || '%'
|
or bookSourceGroup like '%' || :searchKey || '%'
|
||||||
@ -33,32 +42,42 @@ interface BookSourceDao {
|
|||||||
or bookSourceComment like '%' || :searchKey || '%')
|
or bookSourceComment like '%' || :searchKey || '%')
|
||||||
order by customOrder asc"""
|
order by customOrder asc"""
|
||||||
)
|
)
|
||||||
fun flowSearchEnabled(searchKey: String): Flow<List<BookSource>>
|
fun flowSearchEnabled(searchKey: String): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""select * from book_sources
|
"""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources
|
||||||
where bookSourceGroup = :searchKey
|
where bookSourceGroup = :searchKey
|
||||||
or bookSourceGroup like :searchKey || ',%'
|
or bookSourceGroup like :searchKey || ',%'
|
||||||
or bookSourceGroup like '%,' || :searchKey
|
or bookSourceGroup like '%,' || :searchKey
|
||||||
or bookSourceGroup like '%,' || :searchKey || ',%'
|
or bookSourceGroup like '%,' || :searchKey || ',%'
|
||||||
order by customOrder asc"""
|
order by customOrder asc"""
|
||||||
)
|
)
|
||||||
fun flowGroupSearch(searchKey: String): Flow<List<BookSource>>
|
fun flowGroupSearch(searchKey: String): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query("select * from book_sources where enabled = 1 order by customOrder asc")
|
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
fun flowEnabled(): Flow<List<BookSource>>
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources where enabled = 1 order by customOrder asc""")
|
||||||
|
fun flowEnabled(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query("select * from book_sources where enabled = 0 order by customOrder asc")
|
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
fun flowDisabled(): Flow<List<BookSource>>
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources where enabled = 0 order by customOrder asc""")
|
||||||
|
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 * from book_sources where loginUrl is not null and loginUrl != ''")
|
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
fun flowLogin(): Flow<List<BookSource>>
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources where loginUrl is not null and loginUrl != ''""")
|
||||||
|
fun flowLogin(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query("select * from book_sources where bookSourceGroup is null or bookSourceGroup = '' or bookSourceGroup like '%未分组%'")
|
@Query("""select bookSourceUrl, bookSourceName, bookSourceGroup, customOrder, enabled, enabledExplore,
|
||||||
fun flowNoGroup(): Flow<List<BookSource>>
|
trim(loginUrl) <> '' hasLoginUrl, lastUpdateTime, respondTime, weight, trim(exploreUrl) <> '' hasExploreUrl
|
||||||
|
from book_sources where bookSourceGroup is null or bookSourceGroup = '' or bookSourceGroup like '%未分组%'""")
|
||||||
|
fun flowNoGroup(): Flow<List<BookSourcePart>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""select * from book_sources
|
"""select * from book_sources
|
||||||
@ -155,12 +174,59 @@ interface BookSourceDao {
|
|||||||
@Query("delete from book_sources where bookSourceUrl = :key")
|
@Query("delete from book_sources where bookSourceUrl = :key")
|
||||||
fun delete(key: String)
|
fun delete(key: String)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
fun delete(bookSources: List<BookSourcePart>) {
|
||||||
|
for (bs in bookSources) {
|
||||||
|
delete(bs.bookSourceUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@get:Query("select min(customOrder) from book_sources")
|
@get:Query("select min(customOrder) from book_sources")
|
||||||
val minOrder: Int
|
val minOrder: Int
|
||||||
|
|
||||||
@get:Query("select max(customOrder) from book_sources")
|
@get:Query("select max(customOrder) from book_sources")
|
||||||
val maxOrder: Int
|
val maxOrder: Int
|
||||||
|
|
||||||
|
@Query("update book_sources set enabled = :enable where bookSourceUrl = :bookSourceUrl")
|
||||||
|
fun enable(bookSourceUrl: String, enable: Boolean)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
fun enable(enable: Boolean, bookSources: List<BookSourcePart>) {
|
||||||
|
for (bs in bookSources) {
|
||||||
|
enable(bs.bookSourceUrl, enable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query("update book_sources set enabledExplore = :enable where bookSourceUrl = :bookSourceUrl")
|
||||||
|
fun enableExplore(bookSourceUrl: String, enable: Boolean)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
fun enableExplore(enable: Boolean, bookSources: List<BookSourcePart>) {
|
||||||
|
for (bs in bookSources) {
|
||||||
|
enableExplore(bs.bookSourceUrl, enable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query("update book_sources set customOrder = :customOrder where bookSourceUrl = :bookSourceUrl")
|
||||||
|
fun upOrder(bookSourceUrl: String, customOrder: Int)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
fun upOrder(bookSources: List<BookSourcePart>) {
|
||||||
|
for (bs in bookSources) {
|
||||||
|
upOrder(bs.bookSourceUrl, bs.customOrder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query("update book_sources set bookSourceGroup = :bookSourceGroup where bookSourceUrl = :bookSourceUrl")
|
||||||
|
fun upGroup(bookSourceUrl: String, bookSourceGroup: String)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
fun upGroup(bookSources: List<BookSourcePart>) {
|
||||||
|
for (bs in bookSources) {
|
||||||
|
bs.bookSourceGroup?.let { upGroup(bs.bookSourceUrl, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun dealGroups(list: List<String>): List<String> {
|
private fun dealGroups(list: List<String>): List<String> {
|
||||||
val groups = linkedSetOf<String>()
|
val groups = linkedSetOf<String>()
|
||||||
list.forEach {
|
list.forEach {
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
package io.legado.app.data.entities
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import io.legado.app.constant.AppPattern
|
||||||
|
import io.legado.app.data.appDb
|
||||||
|
import io.legado.app.utils.splitNotBlank
|
||||||
|
|
||||||
|
|
||||||
|
data class BookSourcePart(
|
||||||
|
// 地址,包括 http/https
|
||||||
|
var bookSourceUrl: String = "",
|
||||||
|
// 名称
|
||||||
|
var bookSourceName: String = "",
|
||||||
|
// 分组
|
||||||
|
var bookSourceGroup: String? = null,
|
||||||
|
// 手动排序编号
|
||||||
|
var customOrder: Int = 0,
|
||||||
|
// 是否启用
|
||||||
|
var enabled: Boolean = true,
|
||||||
|
// 启用发现
|
||||||
|
var enabledExplore: Boolean = true,
|
||||||
|
// 是否有登录地址
|
||||||
|
var hasLoginUrl: Boolean = false,
|
||||||
|
// 最后更新时间,用于排序
|
||||||
|
var lastUpdateTime: Long = 0,
|
||||||
|
// 响应时间,用于排序
|
||||||
|
var respondTime: Long = 180000L,
|
||||||
|
// 智能排序的权重
|
||||||
|
var weight: Int = 0,
|
||||||
|
// 是否有发现url
|
||||||
|
var hasExploreUrl: Boolean = false
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return bookSourceUrl.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return if (other is BookSourcePart) other.bookSourceUrl == bookSourceUrl else false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDisPlayNameGroup(): String {
|
||||||
|
return if (bookSourceGroup.isNullOrBlank()) {
|
||||||
|
bookSourceName
|
||||||
|
} else {
|
||||||
|
String.format("%s (%s)", bookSourceName, bookSourceGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBookSource(): BookSource? {
|
||||||
|
return appDb.bookSourceDao.getBookSource(bookSourceUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addGroup(groups: String) {
|
||||||
|
bookSourceGroup?.splitNotBlank(AppPattern.splitGroupRegex)?.toHashSet()?.let {
|
||||||
|
it.addAll(groups.splitNotBlank(AppPattern.splitGroupRegex))
|
||||||
|
bookSourceGroup = TextUtils.join(",", it)
|
||||||
|
}
|
||||||
|
if (bookSourceGroup.isNullOrBlank()) bookSourceGroup = groups
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeGroup(groups: String) {
|
||||||
|
bookSourceGroup?.splitNotBlank(AppPattern.splitGroupRegex)?.toHashSet()?.let {
|
||||||
|
it.removeAll(groups.splitNotBlank(AppPattern.splitGroupRegex).toSet())
|
||||||
|
bookSourceGroup = TextUtils.join(",", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<BookSourcePart>.toBookSource(): List<BookSource> {
|
||||||
|
return mapNotNull { it.getBookSource() }
|
||||||
|
}
|
@ -13,6 +13,7 @@ import android.webkit.WebViewClient
|
|||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.utils.runOnUI
|
import io.legado.app.utils.runOnUI
|
||||||
import kotlinx.coroutines.Runnable
|
import kotlinx.coroutines.Runnable
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@ -80,6 +81,7 @@ class BackstageWebView(
|
|||||||
} else {
|
} else {
|
||||||
webView.loadDataWithBaseURL(url, html, "text/html", getEncoding(), url)
|
webView.loadDataWithBaseURL(url, html, "text/html", getEncoding(), url)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> if (headerMap == null) {
|
else -> if (headerMap == null) {
|
||||||
webView.loadUrl(url!!)
|
webView.loadUrl(url!!)
|
||||||
} else {
|
} else {
|
||||||
@ -131,10 +133,15 @@ class BackstageWebView(
|
|||||||
|
|
||||||
private inner class HtmlWebViewClient : WebViewClient() {
|
private inner class HtmlWebViewClient : WebViewClient() {
|
||||||
|
|
||||||
|
var runnable: EvalJsRunnable? = null
|
||||||
|
|
||||||
override fun onPageFinished(view: WebView, url: String) {
|
override fun onPageFinished(view: WebView, url: String) {
|
||||||
setCookie(url)
|
setCookie(url)
|
||||||
val runnable = EvalJsRunnable(view, url, getJs())
|
if (runnable == null) {
|
||||||
mHandler.postDelayed(runnable, 1000)
|
runnable = EvalJsRunnable(view, url, getJs())
|
||||||
|
}
|
||||||
|
mHandler.removeCallbacks(runnable!!)
|
||||||
|
mHandler.postDelayed(runnable!!, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("WebViewClientOnReceivedSslError")
|
@SuppressLint("WebViewClientOnReceivedSslError")
|
||||||
@ -157,30 +164,35 @@ class BackstageWebView(
|
|||||||
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
||||||
override fun run() {
|
override fun run() {
|
||||||
mWebView.get()?.evaluateJavascript(mJavaScript) {
|
mWebView.get()?.evaluateJavascript(mJavaScript) {
|
||||||
if (it.isNotEmpty() && it != "null") {
|
handleResult(it)
|
||||||
val content = StringEscapeUtils.unescapeJson(it)
|
|
||||||
.replace("^\"|\"$".toRegex(), "")
|
|
||||||
try {
|
|
||||||
val response = StrResponse(url, content)
|
|
||||||
callback?.onResult(response)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
callback?.onError(e)
|
|
||||||
}
|
|
||||||
mHandler.removeCallbacks(this)
|
|
||||||
destroy()
|
|
||||||
return@evaluateJavascript
|
|
||||||
}
|
|
||||||
if (retry > 30) {
|
|
||||||
callback?.onError(NoStackTraceException("js执行超时"))
|
|
||||||
mHandler.removeCallbacks(this)
|
|
||||||
destroy()
|
|
||||||
return@evaluateJavascript
|
|
||||||
}
|
|
||||||
retry++
|
|
||||||
mHandler.removeCallbacks(this)
|
|
||||||
mHandler.postDelayed(this, 1000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleResult(result: String) = Coroutine.async {
|
||||||
|
if (result.isNotEmpty() && result != "null") {
|
||||||
|
val content = StringEscapeUtils.unescapeJson(result)
|
||||||
|
.replace(quoteRegex, "")
|
||||||
|
try {
|
||||||
|
val response = StrResponse(url, content)
|
||||||
|
callback?.onResult(response)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
callback?.onError(e)
|
||||||
|
}
|
||||||
|
mHandler.post {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
return@async
|
||||||
|
}
|
||||||
|
if (retry > 30) {
|
||||||
|
callback?.onError(NoStackTraceException("js执行超时"))
|
||||||
|
mHandler.post {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
return@async
|
||||||
|
}
|
||||||
|
retry++
|
||||||
|
mHandler.postDelayed(this@EvalJsRunnable, 1000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class SnifferWebClient : WebViewClient() {
|
private inner class SnifferWebClient : WebViewClient() {
|
||||||
@ -231,6 +243,7 @@ class BackstageWebView(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val JS = "document.documentElement.outerHTML"
|
const val JS = "document.documentElement.outerHTML"
|
||||||
|
private val quoteRegex = "^\"|\"$".toRegex()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Callback {
|
abstract class Callback {
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.IntentAction
|
import io.legado.app.constant.IntentAction
|
||||||
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.help.CacheManager
|
import io.legado.app.help.CacheManager
|
||||||
import io.legado.app.help.IntentData
|
import io.legado.app.help.IntentData
|
||||||
import io.legado.app.service.CheckSourceService
|
import io.legado.app.service.CheckSourceService
|
||||||
@ -22,7 +23,7 @@ object CheckSource {
|
|||||||
var checkContent = CacheManager.get("checkContent")?.toBoolean() ?: true
|
var checkContent = CacheManager.get("checkContent")?.toBoolean() ?: true
|
||||||
val summary get() = upSummary()
|
val summary get() = upSummary()
|
||||||
|
|
||||||
fun start(context: Context, sources: List<BookSource>) {
|
fun start(context: Context, sources: List<BookSourcePart>) {
|
||||||
val selectedIds = sources.map {
|
val selectedIds = sources.map {
|
||||||
it.bookSourceUrl
|
it.bookSourceUrl
|
||||||
}
|
}
|
||||||
|
@ -56,18 +56,17 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
|
|||||||
fun explore() {
|
fun explore() {
|
||||||
val source = bookSource
|
val source = bookSource
|
||||||
val url = exploreUrl
|
val url = exploreUrl
|
||||||
if (source != null && url != null) {
|
if (source == null || url == null) return
|
||||||
WebBook.exploreBook(viewModelScope, source, url, page)
|
WebBook.exploreBook(viewModelScope, source, url, page)
|
||||||
.timeout(if (BuildConfig.DEBUG) 0L else 30000L)
|
.timeout(if (BuildConfig.DEBUG) 0L else 30000L)
|
||||||
.onSuccess(IO) { searchBooks ->
|
.onSuccess(IO) { searchBooks ->
|
||||||
booksData.postValue(searchBooks)
|
booksData.postValue(searchBooks)
|
||||||
appDb.searchBookDao.insert(*searchBooks.toTypedArray())
|
appDb.searchBookDao.insert(*searchBooks.toTypedArray())
|
||||||
page++
|
page++
|
||||||
}.onError {
|
}.onError {
|
||||||
it.printOnDebug()
|
it.printOnDebug()
|
||||||
errorLiveData.postValue(it.stackTraceStr)
|
errorLiveData.postValue(it.stackTraceStr)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import io.legado.app.base.adapter.ItemViewHolder
|
|||||||
import io.legado.app.base.adapter.RecyclerAdapter
|
import io.legado.app.base.adapter.RecyclerAdapter
|
||||||
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.databinding.DialogSourcePickerBinding
|
import io.legado.app.databinding.DialogSourcePickerBinding
|
||||||
import io.legado.app.databinding.Item1lineTextBinding
|
import io.legado.app.databinding.Item1lineTextBinding
|
||||||
import io.legado.app.lib.theme.primaryColor
|
import io.legado.app.lib.theme.primaryColor
|
||||||
@ -81,7 +82,7 @@ class SourcePickerDialog : BaseDialogFragment(R.layout.dialog_source_picker) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class SourceAdapter(context: Context) :
|
inner class SourceAdapter(context: Context) :
|
||||||
RecyclerAdapter<BookSource, Item1lineTextBinding>(context) {
|
RecyclerAdapter<BookSourcePart, Item1lineTextBinding>(context) {
|
||||||
|
|
||||||
override fun getViewBinding(parent: ViewGroup): Item1lineTextBinding {
|
override fun getViewBinding(parent: ViewGroup): Item1lineTextBinding {
|
||||||
return Item1lineTextBinding.inflate(inflater, parent, false).apply {
|
return Item1lineTextBinding.inflate(inflater, parent, false).apply {
|
||||||
@ -92,7 +93,7 @@ class SourcePickerDialog : BaseDialogFragment(R.layout.dialog_source_picker) {
|
|||||||
override fun convert(
|
override fun convert(
|
||||||
holder: ItemViewHolder,
|
holder: ItemViewHolder,
|
||||||
binding: Item1lineTextBinding,
|
binding: Item1lineTextBinding,
|
||||||
item: BookSource,
|
item: BookSourcePart,
|
||||||
payloads: MutableList<Any>
|
payloads: MutableList<Any>
|
||||||
) {
|
) {
|
||||||
binding.textView.text = item.getDisPlayNameGroup()
|
binding.textView.text = item.getDisPlayNameGroup()
|
||||||
@ -101,7 +102,9 @@ class SourcePickerDialog : BaseDialogFragment(R.layout.dialog_source_picker) {
|
|||||||
override fun registerListener(holder: ItemViewHolder, binding: Item1lineTextBinding) {
|
override fun registerListener(holder: ItemViewHolder, binding: Item1lineTextBinding) {
|
||||||
binding.root.onClick {
|
binding.root.onClick {
|
||||||
getItemByLayoutPosition(holder.layoutPosition)?.let {
|
getItemByLayoutPosition(holder.layoutPosition)?.let {
|
||||||
callback?.sourceOnClick(it)
|
it.getBookSource()?.let { source ->
|
||||||
|
callback?.sourceOnClick(source)
|
||||||
|
}
|
||||||
dismissAllowingStateLoss()
|
dismissAllowingStateLoss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
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.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.utils.splitNotBlank
|
import io.legado.app.utils.splitNotBlank
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
@ -20,6 +21,10 @@ data class SearchScope(private var scope: String) {
|
|||||||
"${source.bookSourceName.replace(":", "")}::${source.bookSourceUrl}"
|
"${source.bookSourceName.replace(":", "")}::${source.bookSourceUrl}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
constructor(source: BookSourcePart) : this(
|
||||||
|
"${source.bookSourceName.replace(":", "")}::${source.bookSourceUrl}"
|
||||||
|
)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ 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.BookSource
|
import io.legado.app.data.entities.BookSource
|
||||||
|
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
|
||||||
@ -143,54 +145,66 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
mode = HandleFileContract.FILE
|
mode = HandleFileContract.FILE
|
||||||
allowExtensions = arrayOf("txt", "json")
|
allowExtensions = arrayOf("txt", "json")
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_import_onLine -> showImportDialog()
|
R.id.menu_import_onLine -> showImportDialog()
|
||||||
R.id.menu_sort_manual -> {
|
R.id.menu_sort_manual -> {
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
sortCheck(Sort.Default)
|
sortCheck(Sort.Default)
|
||||||
upBookSource(searchView.query?.toString())
|
upBookSource(searchView.query?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_sort_auto -> {
|
R.id.menu_sort_auto -> {
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
sortCheck(Sort.Weight)
|
sortCheck(Sort.Weight)
|
||||||
upBookSource(searchView.query?.toString())
|
upBookSource(searchView.query?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_sort_name -> {
|
R.id.menu_sort_name -> {
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
sortCheck(Sort.Name)
|
sortCheck(Sort.Name)
|
||||||
upBookSource(searchView.query?.toString())
|
upBookSource(searchView.query?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_sort_url -> {
|
R.id.menu_sort_url -> {
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
sortCheck(Sort.Url)
|
sortCheck(Sort.Url)
|
||||||
upBookSource(searchView.query?.toString())
|
upBookSource(searchView.query?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_sort_time -> {
|
R.id.menu_sort_time -> {
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
sortCheck(Sort.Update)
|
sortCheck(Sort.Update)
|
||||||
upBookSource(searchView.query?.toString())
|
upBookSource(searchView.query?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_sort_respondTime -> {
|
R.id.menu_sort_respondTime -> {
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
sortCheck(Sort.Respond)
|
sortCheck(Sort.Respond)
|
||||||
upBookSource(searchView.query?.toString())
|
upBookSource(searchView.query?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_sort_enable -> {
|
R.id.menu_sort_enable -> {
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
sortCheck(Sort.Enable)
|
sortCheck(Sort.Enable)
|
||||||
upBookSource(searchView.query?.toString())
|
upBookSource(searchView.query?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_enabled_group -> {
|
R.id.menu_enabled_group -> {
|
||||||
searchView.setQuery(getString(R.string.enabled), true)
|
searchView.setQuery(getString(R.string.enabled), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_disabled_group -> {
|
R.id.menu_disabled_group -> {
|
||||||
searchView.setQuery(getString(R.string.disabled), true)
|
searchView.setQuery(getString(R.string.disabled), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_group_login -> {
|
R.id.menu_group_login -> {
|
||||||
searchView.setQuery(getString(R.string.need_login), true)
|
searchView.setQuery(getString(R.string.need_login), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_group_null -> {
|
R.id.menu_group_null -> {
|
||||||
searchView.setQuery(getString(R.string.no_group), true)
|
searchView.setQuery(getString(R.string.no_group), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_help -> showHelp()
|
R.id.menu_help -> showHelp()
|
||||||
}
|
}
|
||||||
if (item.groupId == R.id.source_group) {
|
if (item.groupId == R.id.source_group) {
|
||||||
@ -228,22 +242,28 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
searchKey.isNullOrEmpty() -> {
|
searchKey.isNullOrEmpty() -> {
|
||||||
appDb.bookSourceDao.flowAll()
|
appDb.bookSourceDao.flowAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
searchKey == getString(R.string.enabled) -> {
|
searchKey == getString(R.string.enabled) -> {
|
||||||
appDb.bookSourceDao.flowEnabled()
|
appDb.bookSourceDao.flowEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
searchKey == getString(R.string.disabled) -> {
|
searchKey == getString(R.string.disabled) -> {
|
||||||
appDb.bookSourceDao.flowDisabled()
|
appDb.bookSourceDao.flowDisabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
searchKey == getString(R.string.need_login) -> {
|
searchKey == getString(R.string.need_login) -> {
|
||||||
appDb.bookSourceDao.flowLogin()
|
appDb.bookSourceDao.flowLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
searchKey == getString(R.string.no_group) -> {
|
searchKey == getString(R.string.no_group) -> {
|
||||||
appDb.bookSourceDao.flowNoGroup()
|
appDb.bookSourceDao.flowNoGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
searchKey.startsWith("group:") -> {
|
searchKey.startsWith("group:") -> {
|
||||||
val key = searchKey.substringAfter("group:")
|
val key = searchKey.substringAfter("group:")
|
||||||
appDb.bookSourceDao.flowGroupSearch(key)
|
appDb.bookSourceDao.flowGroupSearch(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
appDb.bookSourceDao.flowSearch(searchKey)
|
appDb.bookSourceDao.flowSearch(searchKey)
|
||||||
}
|
}
|
||||||
@ -253,6 +273,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
Sort.Name -> data.sortedWith { o1, o2 ->
|
Sort.Name -> data.sortedWith { o1, o2 ->
|
||||||
o1.bookSourceName.cnCompare(o2.bookSourceName)
|
o1.bookSourceName.cnCompare(o2.bookSourceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Sort.Url -> data.sortedBy { it.bookSourceUrl }
|
Sort.Url -> data.sortedBy { it.bookSourceUrl }
|
||||||
Sort.Update -> data.sortedByDescending { it.lastUpdateTime }
|
Sort.Update -> data.sortedByDescending { it.lastUpdateTime }
|
||||||
Sort.Respond -> data.sortedBy { it.respondTime }
|
Sort.Respond -> data.sortedBy { it.respondTime }
|
||||||
@ -263,6 +284,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
}
|
}
|
||||||
sort
|
sort
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> data
|
else -> data
|
||||||
}
|
}
|
||||||
else when (sort) {
|
else when (sort) {
|
||||||
@ -270,6 +292,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
Sort.Name -> data.sortedWith { o1, o2 ->
|
Sort.Name -> data.sortedWith { o1, o2 ->
|
||||||
o2.bookSourceName.cnCompare(o1.bookSourceName)
|
o2.bookSourceName.cnCompare(o1.bookSourceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Sort.Url -> data.sortedByDescending { it.bookSourceUrl }
|
Sort.Url -> data.sortedByDescending { it.bookSourceUrl }
|
||||||
Sort.Update -> data.sortedBy { it.lastUpdateTime }
|
Sort.Update -> data.sortedBy { it.lastUpdateTime }
|
||||||
Sort.Respond -> data.sortedByDescending { it.respondTime }
|
Sort.Respond -> data.sortedByDescending { it.respondTime }
|
||||||
@ -280,6 +303,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
}
|
}
|
||||||
sort
|
sort
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> data.reversed()
|
else -> data.reversed()
|
||||||
}
|
}
|
||||||
}.catch {
|
}.catch {
|
||||||
@ -330,7 +354,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
|
|
||||||
override fun onClickSelectBarMainAction() {
|
override fun onClickSelectBarMainAction() {
|
||||||
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
|
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
|
||||||
yesButton { viewModel.del(*adapter.selection.toTypedArray()) }
|
yesButton { viewModel.del(adapter.selection) }
|
||||||
noButton()
|
noButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,7 +377,7 @@ 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) { file ->
|
R.id.menu_export_selection -> viewModel.saveToFile(adapter.selection.toBookSource()) { file ->
|
||||||
exportDir.launch {
|
exportDir.launch {
|
||||||
mode = HandleFileContract.EXPORT
|
mode = HandleFileContract.EXPORT
|
||||||
fileData = HandleFileContract.FileData(
|
fileData = HandleFileContract.FileData(
|
||||||
@ -363,9 +387,11 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.menu_share_source -> viewModel.saveToFile(adapter.selection) {
|
|
||||||
|
R.id.menu_share_source -> viewModel.saveToFile(adapter.selection.toBookSource()) {
|
||||||
share(it)
|
share(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_check_selected_interval -> adapter.checkSelectedInterval()
|
R.id.menu_check_selected_interval -> adapter.checkSelectedInterval()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -386,9 +412,11 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
CheckSource.keyword = it
|
CheckSource.keyword = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckSource.start(this@BookSourceActivity, adapter.selection)
|
val selectItems = adapter.selection
|
||||||
val firstItem = adapter.getItems().indexOf(adapter.selection.firstOrNull())
|
CheckSource.start(this@BookSourceActivity, selectItems)
|
||||||
val lastItem = adapter.getItems().indexOf(adapter.selection.lastOrNull())
|
val adapterItems = adapter.getItems()
|
||||||
|
val firstItem = adapterItems.indexOf(selectItems.firstOrNull())
|
||||||
|
val lastItem = adapterItems.indexOf(selectItems.lastOrNull())
|
||||||
Debug.isChecking = firstItem >= 0 && lastItem >= 0
|
Debug.isChecking = firstItem >= 0 && lastItem >= 0
|
||||||
checkMessageRefreshJob(firstItem, lastItem).start()
|
checkMessageRefreshJob(firstItem, lastItem).start()
|
||||||
}
|
}
|
||||||
@ -587,45 +615,49 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun del(bookSource: BookSource) {
|
override fun del(bookSource: BookSourcePart) {
|
||||||
alert(R.string.draw) {
|
alert(R.string.draw) {
|
||||||
setMessage(getString(R.string.sure_del) + "\n" + bookSource.bookSourceName)
|
setMessage(getString(R.string.sure_del) + "\n" + bookSource.bookSourceName)
|
||||||
noButton()
|
noButton()
|
||||||
yesButton {
|
yesButton {
|
||||||
viewModel.del(bookSource)
|
viewModel.del(listOf(bookSource))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(vararg bookSource: BookSource) {
|
override fun edit(bookSource: BookSourcePart) {
|
||||||
viewModel.update(*bookSource)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun edit(bookSource: BookSource) {
|
|
||||||
startActivity<BookSourceEditActivity> {
|
startActivity<BookSourceEditActivity> {
|
||||||
putExtra("sourceUrl", bookSource.bookSourceUrl)
|
putExtra("sourceUrl", bookSource.bookSourceUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun upOrder(items: List<BookSource>) {
|
override fun upOrder(items: List<BookSourcePart>) {
|
||||||
viewModel.upOrder(items)
|
viewModel.upOrder(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toTop(bookSource: BookSource) {
|
override fun enable(enable: Boolean, bookSource: BookSourcePart) {
|
||||||
|
viewModel.enable(enable, listOf(bookSource))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun enableExplore(enable: Boolean, bookSource: BookSourcePart) {
|
||||||
|
viewModel.enableExplore(enable, listOf(bookSource))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toTop(bookSource: BookSourcePart) {
|
||||||
viewModel.topSource(bookSource)
|
viewModel.topSource(bookSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toBottom(bookSource: BookSource) {
|
override fun toBottom(bookSource: BookSourcePart) {
|
||||||
viewModel.bottomSource(bookSource)
|
viewModel.bottomSource(bookSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchBook(bookSource: BookSource) {
|
override fun searchBook(bookSource: BookSourcePart) {
|
||||||
startActivity<SearchActivity> {
|
startActivity<SearchActivity> {
|
||||||
putExtra("searchScope", SearchScope(bookSource).toString())
|
putExtra("searchScope", SearchScope(bookSource).toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun debug(bookSource: BookSource) {
|
override fun debug(bookSource: BookSourcePart) {
|
||||||
startActivity<BookSourceDebugActivity> {
|
startActivity<BookSourceDebugActivity> {
|
||||||
putExtra("key", bookSource.bookSourceUrl)
|
putExtra("key", bookSource.bookSourceUrl)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.adapter.ItemViewHolder
|
import io.legado.app.base.adapter.ItemViewHolder
|
||||||
import io.legado.app.base.adapter.RecyclerAdapter
|
import io.legado.app.base.adapter.RecyclerAdapter
|
||||||
import io.legado.app.data.entities.BookSource
|
import io.legado.app.data.entities.BookSourcePart
|
||||||
import io.legado.app.databinding.ItemBookSourceBinding
|
import io.legado.app.databinding.ItemBookSourceBinding
|
||||||
import io.legado.app.lib.theme.backgroundColor
|
import io.legado.app.lib.theme.backgroundColor
|
||||||
import io.legado.app.model.Debug
|
import io.legado.app.model.Debug
|
||||||
@ -28,34 +28,34 @@ import java.util.*
|
|||||||
|
|
||||||
|
|
||||||
class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
||||||
RecyclerAdapter<BookSource, ItemBookSourceBinding>(context),
|
RecyclerAdapter<BookSourcePart, ItemBookSourceBinding>(context),
|
||||||
ItemTouchCallback.Callback {
|
ItemTouchCallback.Callback {
|
||||||
|
|
||||||
private val selected = linkedSetOf<BookSource>()
|
private val selected = linkedSetOf<BookSourcePart>()
|
||||||
private val finalMessageRegex = Regex("成功|失败")
|
private val finalMessageRegex = Regex("成功|失败")
|
||||||
|
|
||||||
val selection: List<BookSource>
|
val selection: List<BookSourcePart>
|
||||||
get() {
|
get() {
|
||||||
return getItems().filter {
|
return getItems().filter {
|
||||||
selected.contains(it)
|
selected.contains(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val diffItemCallback = object : DiffUtil.ItemCallback<BookSource>() {
|
val diffItemCallback = object : DiffUtil.ItemCallback<BookSourcePart>() {
|
||||||
|
|
||||||
override fun areItemsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
|
override fun areItemsTheSame(oldItem: BookSourcePart, newItem: BookSourcePart): Boolean {
|
||||||
return oldItem.bookSourceUrl == newItem.bookSourceUrl
|
return oldItem.bookSourceUrl == newItem.bookSourceUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
|
override fun areContentsTheSame(oldItem: BookSourcePart, newItem: BookSourcePart): Boolean {
|
||||||
return oldItem.bookSourceName == newItem.bookSourceName
|
return oldItem.bookSourceName == newItem.bookSourceName
|
||||||
&& oldItem.bookSourceGroup == newItem.bookSourceGroup
|
&& oldItem.bookSourceGroup == newItem.bookSourceGroup
|
||||||
&& oldItem.enabled == newItem.enabled
|
&& oldItem.enabled == newItem.enabled
|
||||||
&& oldItem.enabledExplore == newItem.enabledExplore
|
&& oldItem.enabledExplore == newItem.enabledExplore
|
||||||
&& oldItem.exploreUrl == newItem.exploreUrl
|
&& oldItem.hasExploreUrl == newItem.hasExploreUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getChangePayload(oldItem: BookSource, newItem: BookSource): Any? {
|
override fun getChangePayload(oldItem: BookSourcePart, newItem: BookSourcePart): Any? {
|
||||||
val payload = Bundle()
|
val payload = Bundle()
|
||||||
if (oldItem.bookSourceName != newItem.bookSourceName
|
if (oldItem.bookSourceName != newItem.bookSourceName
|
||||||
|| oldItem.bookSourceGroup != newItem.bookSourceGroup
|
|| oldItem.bookSourceGroup != newItem.bookSourceGroup
|
||||||
@ -66,7 +66,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
payload.putBoolean("enabled", newItem.enabled)
|
payload.putBoolean("enabled", newItem.enabled)
|
||||||
}
|
}
|
||||||
if (oldItem.enabledExplore != newItem.enabledExplore ||
|
if (oldItem.enabledExplore != newItem.enabledExplore ||
|
||||||
oldItem.exploreUrl != newItem.exploreUrl
|
oldItem.hasExploreUrl != newItem.hasExploreUrl
|
||||||
) {
|
) {
|
||||||
payload.putBoolean("upExplore", true)
|
payload.putBoolean("upExplore", true)
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
override fun convert(
|
override fun convert(
|
||||||
holder: ItemViewHolder,
|
holder: ItemViewHolder,
|
||||||
binding: ItemBookSourceBinding,
|
binding: ItemBookSourceBinding,
|
||||||
item: BookSource,
|
item: BookSourcePart,
|
||||||
payloads: MutableList<Any>
|
payloads: MutableList<Any>
|
||||||
) {
|
) {
|
||||||
binding.run {
|
binding.run {
|
||||||
@ -117,7 +117,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
getItem(holder.layoutPosition)?.let {
|
getItem(holder.layoutPosition)?.let {
|
||||||
if (view.isPressed) {
|
if (view.isPressed) {
|
||||||
it.enabled = checked
|
it.enabled = checked
|
||||||
callBack.update(it)
|
callBack.enable(checked, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
val popupMenu = PopupMenu(context, view)
|
val popupMenu = PopupMenu(context, view)
|
||||||
popupMenu.inflate(R.menu.book_source_item)
|
popupMenu.inflate(R.menu.book_source_item)
|
||||||
val qyMenu = popupMenu.menu.findItem(R.id.menu_enable_explore)
|
val qyMenu = popupMenu.menu.findItem(R.id.menu_enable_explore)
|
||||||
if (source.exploreUrl.isNullOrEmpty()) {
|
if (!source.hasExploreUrl) {
|
||||||
qyMenu.isVisible = false
|
qyMenu.isVisible = false
|
||||||
} else {
|
} else {
|
||||||
if (source.enabledExplore) {
|
if (source.enabledExplore) {
|
||||||
@ -163,7 +163,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val loginMenu = popupMenu.menu.findItem(R.id.menu_login)
|
val loginMenu = popupMenu.menu.findItem(R.id.menu_login)
|
||||||
loginMenu.isVisible = !source.loginUrl.isNullOrBlank()
|
loginMenu.isVisible = source.hasLoginUrl
|
||||||
popupMenu.setOnMenuItemClickListener { menuItem ->
|
popupMenu.setOnMenuItemClickListener { menuItem ->
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.menu_top -> callBack.toTop(source)
|
R.id.menu_top -> callBack.toTop(source)
|
||||||
@ -172,14 +172,16 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
putExtra("type", "bookSource")
|
putExtra("type", "bookSource")
|
||||||
putExtra("key", source.bookSourceUrl)
|
putExtra("key", source.bookSourceUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_search -> callBack.searchBook(source)
|
R.id.menu_search -> callBack.searchBook(source)
|
||||||
R.id.menu_debug_source -> callBack.debug(source)
|
R.id.menu_debug_source -> callBack.debug(source)
|
||||||
R.id.menu_del -> {
|
R.id.menu_del -> {
|
||||||
callBack.del(source)
|
callBack.del(source)
|
||||||
selected.remove(source)
|
selected.remove(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_enable_explore -> {
|
R.id.menu_enable_explore -> {
|
||||||
callBack.update(source.copy(enabledExplore = !source.enabledExplore))
|
callBack.enableExplore(!source.enabledExplore, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@ -187,16 +189,18 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
popupMenu.show()
|
popupMenu.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun upShowExplore(iv: ImageView, source: BookSource) {
|
private fun upShowExplore(iv: ImageView, source: BookSourcePart) {
|
||||||
when {
|
when {
|
||||||
source.exploreUrl.isNullOrEmpty() -> {
|
!source.hasExploreUrl -> {
|
||||||
iv.invisible()
|
iv.invisible()
|
||||||
}
|
}
|
||||||
|
|
||||||
source.enabledExplore -> {
|
source.enabledExplore -> {
|
||||||
iv.setColorFilter(Color.GREEN)
|
iv.setColorFilter(Color.GREEN)
|
||||||
iv.visible()
|
iv.visible()
|
||||||
iv.contentDescription = context.getString(R.string.tag_explore_enabled)
|
iv.contentDescription = context.getString(R.string.tag_explore_enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
iv.setColorFilter(Color.RED)
|
iv.setColorFilter(Color.RED)
|
||||||
iv.visible()
|
iv.visible()
|
||||||
@ -205,7 +209,10 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun upCheckSourceMessage(binding: ItemBookSourceBinding, item: BookSource) = binding.run {
|
private fun upCheckSourceMessage(
|
||||||
|
binding: ItemBookSourceBinding,
|
||||||
|
item: BookSourcePart
|
||||||
|
) = binding.run {
|
||||||
val msg = Debug.debugMessageMap[item.bookSourceUrl] ?: ""
|
val msg = Debug.debugMessageMap[item.bookSourceUrl] ?: ""
|
||||||
ivDebugText.text = msg
|
ivDebugText.text = msg
|
||||||
val isEmpty = msg.isEmpty()
|
val isEmpty = msg.isEmpty()
|
||||||
@ -274,7 +281,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private val movedItems = hashSetOf<BookSource>()
|
private val movedItems = hashSetOf<BookSourcePart>()
|
||||||
|
|
||||||
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||||
if (movedItems.isNotEmpty()) {
|
if (movedItems.isNotEmpty()) {
|
||||||
@ -285,19 +292,19 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
if (movedItems.size > sortNumberSet.size) {
|
if (movedItems.size > sortNumberSet.size) {
|
||||||
callBack.upOrder(getItems())
|
callBack.upOrder(getItems())
|
||||||
} else {
|
} else {
|
||||||
callBack.update(*movedItems.toTypedArray())
|
callBack.upOrder(movedItems.toList())
|
||||||
}
|
}
|
||||||
movedItems.clear()
|
movedItems.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val dragSelectCallback: DragSelectTouchHelper.Callback =
|
val dragSelectCallback: DragSelectTouchHelper.Callback =
|
||||||
object : DragSelectTouchHelper.AdvanceCallback<BookSource>(Mode.ToggleAndReverse) {
|
object : DragSelectTouchHelper.AdvanceCallback<BookSourcePart>(Mode.ToggleAndReverse) {
|
||||||
override fun currentSelectedId(): MutableSet<BookSource> {
|
override fun currentSelectedId(): MutableSet<BookSourcePart> {
|
||||||
return selected
|
return selected
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position: Int): BookSource {
|
override fun getItemId(position: Int): BookSourcePart {
|
||||||
return getItem(position)!!
|
return getItem(position)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,14 +324,15 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface CallBack {
|
interface CallBack {
|
||||||
fun del(bookSource: BookSource)
|
fun del(bookSource: BookSourcePart)
|
||||||
fun edit(bookSource: BookSource)
|
fun edit(bookSource: BookSourcePart)
|
||||||
fun update(vararg bookSource: BookSource)
|
fun toTop(bookSource: BookSourcePart)
|
||||||
fun toTop(bookSource: BookSource)
|
fun toBottom(bookSource: BookSourcePart)
|
||||||
fun toBottom(bookSource: BookSource)
|
fun searchBook(bookSource: BookSourcePart)
|
||||||
fun searchBook(bookSource: BookSource)
|
fun debug(bookSource: BookSourcePart)
|
||||||
fun debug(bookSource: BookSource)
|
fun upOrder(items: List<BookSourcePart>)
|
||||||
fun upOrder(items: List<BookSource>)
|
fun enable(enable: Boolean, bookSource: BookSourcePart)
|
||||||
|
fun enableExplore(enable: Boolean, bookSource: BookSourcePart)
|
||||||
fun upCountView()
|
fun upCountView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import android.text.TextUtils
|
|||||||
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.help.config.SourceConfig
|
import io.legado.app.help.config.SourceConfig
|
||||||
import io.legado.app.utils.*
|
import io.legado.app.utils.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -16,31 +17,31 @@ import java.io.FileOutputStream
|
|||||||
*/
|
*/
|
||||||
class BookSourceViewModel(application: Application) : BaseViewModel(application) {
|
class BookSourceViewModel(application: Application) : BaseViewModel(application) {
|
||||||
|
|
||||||
fun topSource(vararg sources: BookSource) {
|
fun topSource(vararg sources: BookSourcePart) {
|
||||||
execute {
|
execute {
|
||||||
sources.sortBy { it.customOrder }
|
sources.sortBy { it.customOrder }
|
||||||
val minOrder = appDb.bookSourceDao.minOrder - 1
|
val minOrder = appDb.bookSourceDao.minOrder - 1
|
||||||
val array = Array(sources.size) {
|
val array = sources.mapIndexed { index, it ->
|
||||||
sources[it].copy(customOrder = minOrder - it)
|
it.copy(customOrder = minOrder - index)
|
||||||
}
|
}
|
||||||
appDb.bookSourceDao.update(*array)
|
appDb.bookSourceDao.upOrder(array)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bottomSource(vararg sources: BookSource) {
|
fun bottomSource(vararg sources: BookSourcePart) {
|
||||||
execute {
|
execute {
|
||||||
sources.sortBy { it.customOrder }
|
sources.sortBy { it.customOrder }
|
||||||
val maxOrder = appDb.bookSourceDao.maxOrder + 1
|
val maxOrder = appDb.bookSourceDao.maxOrder + 1
|
||||||
val array = Array(sources.size) {
|
val array = sources.mapIndexed { index, it ->
|
||||||
sources[it].copy(customOrder = maxOrder + it)
|
it.copy(customOrder = maxOrder + index)
|
||||||
}
|
}
|
||||||
appDb.bookSourceDao.update(*array)
|
appDb.bookSourceDao.upOrder(array)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun del(vararg sources: BookSource) {
|
fun del(sources: List<BookSourcePart>) {
|
||||||
execute {
|
execute {
|
||||||
appDb.bookSourceDao.delete(*sources)
|
appDb.bookSourceDao.delete(sources)
|
||||||
sources.forEach {
|
sources.forEach {
|
||||||
SourceConfig.removeSource(it.bookSourceUrl)
|
SourceConfig.removeSource(it.bookSourceUrl)
|
||||||
}
|
}
|
||||||
@ -51,68 +52,72 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
|
|||||||
execute { appDb.bookSourceDao.update(*bookSource) }
|
execute { appDb.bookSourceDao.update(*bookSource) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upOrder(items: List<BookSource>) {
|
fun upOrder(items: List<BookSourcePart>) {
|
||||||
if (items.isEmpty()) return
|
if (items.isEmpty()) return
|
||||||
execute {
|
execute {
|
||||||
val firstSortNumber = items[0].customOrder
|
val firstSortNumber = items[0].customOrder
|
||||||
items.forEachIndexed { index, bookSource ->
|
items.forEachIndexed { index, bookSource ->
|
||||||
bookSource.customOrder = firstSortNumber + index
|
bookSource.customOrder = firstSortNumber + index
|
||||||
appDb.bookSourceDao.update(bookSource)
|
|
||||||
}
|
}
|
||||||
|
appDb.bookSourceDao.upOrder(items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enableSelection(sources: List<BookSource>) {
|
fun enable(enable: Boolean, items: List<BookSourcePart>) {
|
||||||
execute {
|
execute {
|
||||||
val array = Array(sources.size) {
|
appDb.bookSourceDao.enable(enable, items)
|
||||||
sources[it].copy(enabled = true)
|
|
||||||
}
|
|
||||||
appDb.bookSourceDao.update(*array)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disableSelection(sources: List<BookSource>) {
|
fun enableSelection(sources: List<BookSourcePart>) {
|
||||||
execute {
|
execute {
|
||||||
val array = Array(sources.size) {
|
appDb.bookSourceDao.enable(true, sources)
|
||||||
sources[it].copy(enabled = false)
|
|
||||||
}
|
|
||||||
appDb.bookSourceDao.update(*array)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enableSelectExplore(sources: List<BookSource>) {
|
fun disableSelection(sources: List<BookSourcePart>) {
|
||||||
execute {
|
execute {
|
||||||
val array = Array(sources.size) {
|
appDb.bookSourceDao.enable(false, sources)
|
||||||
sources[it].copy(enabledExplore = true)
|
|
||||||
}
|
|
||||||
appDb.bookSourceDao.update(*array)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disableSelectExplore(sources: List<BookSource>) {
|
fun enableExplore(enable: Boolean, items: List<BookSourcePart>) {
|
||||||
execute {
|
execute {
|
||||||
val array = Array(sources.size) {
|
appDb.bookSourceDao.enableExplore(enable, items)
|
||||||
sources[it].copy(enabledExplore = false)
|
|
||||||
}
|
|
||||||
appDb.bookSourceDao.update(*array)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectionAddToGroups(sources: List<BookSource>, groups: String) {
|
fun enableSelectExplore(sources: List<BookSourcePart>) {
|
||||||
execute {
|
execute {
|
||||||
val array = Array(sources.size) {
|
appDb.bookSourceDao.enableExplore(true, sources)
|
||||||
sources[it].copy().addGroup(groups)
|
|
||||||
}
|
|
||||||
appDb.bookSourceDao.update(*array)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectionRemoveFromGroups(sources: List<BookSource>, groups: String) {
|
fun disableSelectExplore(sources: List<BookSourcePart>) {
|
||||||
execute {
|
execute {
|
||||||
val array = Array(sources.size) {
|
appDb.bookSourceDao.enableExplore(false, sources)
|
||||||
sources[it].copy().removeGroup(groups)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectionAddToGroups(sources: List<BookSourcePart>, groups: String) {
|
||||||
|
execute {
|
||||||
|
val array = sources.map {
|
||||||
|
it.copy().apply {
|
||||||
|
addGroup(groups)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
appDb.bookSourceDao.update(*array)
|
appDb.bookSourceDao.upGroup(array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectionRemoveFromGroups(sources: List<BookSourcePart>, groups: String) {
|
||||||
|
execute {
|
||||||
|
val array = sources.map {
|
||||||
|
it.copy().apply {
|
||||||
|
removeGroup(groups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appDb.bookSourceDao.upGroup(array)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import android.app.Application
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import io.legado.app.R
|
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.help.AppWebDav
|
import io.legado.app.help.AppWebDav
|
||||||
import io.legado.app.help.book.BookHelp
|
import io.legado.app.help.book.BookHelp
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
@ -34,5 +35,12 @@ class ConfigViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shrinkDatabase() {
|
||||||
|
execute {
|
||||||
|
appDb.openHelper.writableDatabase.execSQL("VACUUM")
|
||||||
|
}.onSuccess {
|
||||||
|
context.toastOnUi(R.string.success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -135,6 +135,7 @@ class OtherConfigFragment : PreferenceFragment(),
|
|||||||
|
|
||||||
PreferKey.clearWebViewData -> clearWebViewData()
|
PreferKey.clearWebViewData -> clearWebViewData()
|
||||||
"localPassword" -> alertLocalPassword()
|
"localPassword" -> alertLocalPassword()
|
||||||
|
PreferKey.shrinkDatabase -> shrinkDatabase()
|
||||||
}
|
}
|
||||||
return super.onPreferenceTreeClick(preference)
|
return super.onPreferenceTreeClick(preference)
|
||||||
}
|
}
|
||||||
@ -234,6 +235,15 @@ class OtherConfigFragment : PreferenceFragment(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shrinkDatabase() {
|
||||||
|
alert(R.string.sure, R.string.shrink_database) {
|
||||||
|
okButton {
|
||||||
|
viewModel.shrinkDatabase()
|
||||||
|
}
|
||||||
|
noButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun clearWebViewData() {
|
private fun clearWebViewData() {
|
||||||
alert(R.string.clear_webview_data, R.string.sure_del) {
|
alert(R.string.clear_webview_data, R.string.sure_del) {
|
||||||
okButton {
|
okButton {
|
||||||
|
@ -1110,4 +1110,6 @@
|
|||||||
<string name="custom_export">Custom Export</string>
|
<string name="custom_export">Custom Export</string>
|
||||||
<string name="file_contains_number">The number of chapters contained in each file</string>
|
<string name="file_contains_number">The number of chapters contained in each file</string>
|
||||||
<string name="export_chapter_index">The section index that needs to be exported</string>
|
<string name="export_chapter_index">The section index that needs to be exported</string>
|
||||||
|
<string name="shrink_database">压缩数据库</string>
|
||||||
|
<string name="shrink_database_summary">减小数据库文件的大小</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1113,4 +1113,6 @@
|
|||||||
<string name="custom_export">Custom Export</string>
|
<string name="custom_export">Custom Export</string>
|
||||||
<string name="file_contains_number">The number of chapters contained in each file</string>
|
<string name="file_contains_number">The number of chapters contained in each file</string>
|
||||||
<string name="export_chapter_index">The section index that needs to be exported</string>
|
<string name="export_chapter_index">The section index that needs to be exported</string>
|
||||||
|
<string name="shrink_database">压缩数据库</string>
|
||||||
|
<string name="shrink_database_summary">减小数据库文件的大小</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1113,4 +1113,6 @@
|
|||||||
<string name="custom_export">Custom Export</string>
|
<string name="custom_export">Custom Export</string>
|
||||||
<string name="file_contains_number">The index of chapters contained in each file</string>
|
<string name="file_contains_number">The index of chapters contained in each file</string>
|
||||||
<string name="export_chapter_index">The section index that needs to be exported</string>
|
<string name="export_chapter_index">The section index that needs to be exported</string>
|
||||||
|
<string name="shrink_database">压缩数据库</string>
|
||||||
|
<string name="shrink_database_summary">减小数据库文件的大小</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1110,4 +1110,6 @@
|
|||||||
<string name="custom_export">自定義導出</string>
|
<string name="custom_export">自定義導出</string>
|
||||||
<string name="file_contains_number">每個文件包含的章節數量</string>
|
<string name="file_contains_number">每個文件包含的章節數量</string>
|
||||||
<string name="export_chapter_index">需要輸出的章節</string>
|
<string name="export_chapter_index">需要輸出的章節</string>
|
||||||
|
<string name="shrink_database">压缩数据库</string>
|
||||||
|
<string name="shrink_database_summary">减小数据库文件的大小</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1112,4 +1112,6 @@
|
|||||||
<string name="custom_export">自定義導出</string>
|
<string name="custom_export">自定義導出</string>
|
||||||
<string name="file_contains_number">每個文件包含的章節數量</string>
|
<string name="file_contains_number">每個文件包含的章節數量</string>
|
||||||
<string name="export_chapter_index">需要輸出的章節</string>
|
<string name="export_chapter_index">需要輸出的章節</string>
|
||||||
|
<string name="shrink_database">压缩数据库</string>
|
||||||
|
<string name="shrink_database_summary">减小数据库文件的大小</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1112,4 +1112,6 @@
|
|||||||
<string name="custom_export">自定义导出</string>
|
<string name="custom_export">自定义导出</string>
|
||||||
<string name="file_contains_number">每个文件包含的章节数量</string>
|
<string name="file_contains_number">每个文件包含的章节数量</string>
|
||||||
<string name="export_chapter_index">需要输出的章节</string>
|
<string name="export_chapter_index">需要输出的章节</string>
|
||||||
|
<string name="shrink_database">压缩数据库</string>
|
||||||
|
<string name="shrink_database_summary">减小数据库文件的大小</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1113,4 +1113,6 @@
|
|||||||
<string name="custom_export">Custom Export</string>
|
<string name="custom_export">Custom Export</string>
|
||||||
<string name="file_contains_number">The number of chapters contained in each file</string>
|
<string name="file_contains_number">The number of chapters contained in each file</string>
|
||||||
<string name="export_chapter_index">The section index that needs to be exported</string>
|
<string name="export_chapter_index">The section index that needs to be exported</string>
|
||||||
|
<string name="shrink_database">压缩数据库</string>
|
||||||
|
<string name="shrink_database_summary">减小数据库文件的大小</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -156,6 +156,12 @@
|
|||||||
android:title="@string/clear_webview_data"
|
android:title="@string/clear_webview_data"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<io.legado.app.lib.prefs.Preference
|
||||||
|
android:key="shrinkDatabase"
|
||||||
|
android:summary="@string/shrink_database_summary"
|
||||||
|
android:title="@string/shrink_database"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<io.legado.app.lib.prefs.Preference
|
<io.legado.app.lib.prefs.Preference
|
||||||
android:key="threadCount"
|
android:key="threadCount"
|
||||||
android:title="@string/threads_num_title"
|
android:title="@string/threads_num_title"
|
||||||
|
Loading…
Reference in New Issue
Block a user