mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
优化
This commit is contained in:
parent
a966eea067
commit
85c07b55df
@ -124,7 +124,7 @@ abstract class AbsCallBack(
|
||||
onSuccess(response)
|
||||
|
||||
//打印协议,用于调试
|
||||
DebugLog.i(javaClass.simpleName, "start[${info.negotiatedProtocol}]${info.url}")
|
||||
DebugLog.i(javaClass.simpleName, "onResponseStarted[${info.negotiatedProtocol}][${info.httpStatusCode}]${info.url}")
|
||||
if (eventListener != null) {
|
||||
eventListener.responseHeadersEnd(mCall, response)
|
||||
eventListener.responseBodyStart(mCall)
|
||||
@ -169,6 +169,7 @@ abstract class AbsCallBack(
|
||||
override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
|
||||
canceled.set(true)
|
||||
callbackResults.add(CallbackResult(CallbackStep.ON_CANCELED))
|
||||
//DebugLog.i(javaClass.simpleName, "cancel[${info?.negotiatedProtocol}]${info?.url}")
|
||||
eventListener?.callEnd(mCall)
|
||||
//onError(IOException("Cronet Request Canceled"))
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package io.legado.app.lib.cronet
|
||||
import android.os.Build
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.RequiresApi
|
||||
import io.legado.app.utils.DebugLog
|
||||
import okhttp3.Call
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
@ -20,6 +21,7 @@ class NewCallBack(originalRequest: Request, mCall: Call) : AbsCallBack(originalR
|
||||
@Throws(IOException::class)
|
||||
override fun waitForDone(urlRequest: UrlRequest): Response {
|
||||
urlRequest.start()
|
||||
//DebugLog.i(javaClass.simpleName, "start ${originalRequest.method} ${originalRequest.url}")
|
||||
return if (mCall.timeout().timeoutNanos() > 0) {
|
||||
responseFuture.get(mCall.timeout().timeoutNanos(), TimeUnit.NANOSECONDS)
|
||||
} else {
|
||||
|
@ -69,6 +69,9 @@ object AppWebDav {
|
||||
if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
|
||||
val mAuthorization = Authorization(account, password)
|
||||
checkAuthorization(mAuthorization)
|
||||
WebDav(rootWebDavUrl, mAuthorization).makeAsDir()
|
||||
WebDav(bookProgressUrl, mAuthorization).makeAsDir()
|
||||
WebDav(exportsWebDavUrl, mAuthorization).makeAsDir()
|
||||
val rootBooksUrl = "${rootWebDavUrl}books"
|
||||
defaultBookWebDav = RemoteBookWebDav(rootBooksUrl, mAuthorization)
|
||||
authorization = mAuthorization
|
||||
@ -78,10 +81,7 @@ object AppWebDav {
|
||||
|
||||
@Throws(WebDavException::class)
|
||||
private suspend fun checkAuthorization(authorization: Authorization) {
|
||||
if (!WebDav(rootWebDavUrl, authorization).makeAsDir() ||
|
||||
!WebDav(bookProgressUrl, authorization).makeAsDir() ||
|
||||
!WebDav(exportsWebDavUrl, authorization).makeAsDir()
|
||||
) {
|
||||
if (!WebDav(rootWebDavUrl, authorization).check()) {
|
||||
appCtx.removePref(PreferKey.webDavPassword)
|
||||
appCtx.toastOnUi(R.string.webdav_application_authorization_error)
|
||||
throw WebDavException(appCtx.getString(R.string.webdav_application_authorization_error))
|
||||
@ -213,7 +213,11 @@ object AppWebDav {
|
||||
}
|
||||
|
||||
private fun getProgressUrl(name: String, author: String): String {
|
||||
return bookProgressUrl + UrlUtil.replaceReservedChar("${name}_${author}") + ".json"
|
||||
return bookProgressUrl + getProgressFileName(name, author)
|
||||
}
|
||||
|
||||
private fun getProgressFileName(name: String, author: String): String {
|
||||
return UrlUtil.replaceReservedChar("${name}_${author}") + ".json"
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,15 +233,22 @@ object AppWebDav {
|
||||
return GSON.fromJsonObject<BookProgress>(json).getOrNull()
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
AppLog.put("获取书籍进度失败\n${it.localizedMessage}", it)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun downloadAllBookProgress() {
|
||||
authorization ?: return
|
||||
val authorization = authorization ?: return
|
||||
if (!NetworkUtils.isAvailable()) return
|
||||
val bookProgressFiles = WebDav(bookProgressUrl, authorization).listFiles().map {
|
||||
it.displayName
|
||||
}.toHashSet()
|
||||
appDb.bookDao.all.forEach { book ->
|
||||
val progressFileName = getProgressFileName(book.name, book.author)
|
||||
if (!bookProgressFiles.contains(progressFileName)) return@forEach
|
||||
getBookProgress(book)?.let { bookProgress ->
|
||||
if (bookProgress.durChapterIndex > book.durChapterIndex
|
||||
|| (bookProgress.durChapterIndex == book.durChapterIndex
|
||||
|
@ -229,10 +229,24 @@ open class WebDav(
|
||||
addHeader("Depth", "0")
|
||||
val requestBody = EXISTS.toRequestBody("application/xml".toMediaType())
|
||||
method("PROPFIND", requestBody)
|
||||
}.isSuccessful
|
||||
}.use { it.isSuccessful }
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户名密码是否有效
|
||||
*/
|
||||
suspend fun check(): Boolean {
|
||||
return kotlin.runCatching {
|
||||
webDavClient.newCallResponse {
|
||||
url(url)
|
||||
addHeader("Depth", "0")
|
||||
val requestBody = EXISTS.toRequestBody("application/xml".toMediaType())
|
||||
method("PROPFIND", requestBody)
|
||||
}.use { it.code != 401 }
|
||||
}.getOrDefault(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自己的URL,在远程处创建对应的文件夹
|
||||
* @return 是否创建成功
|
||||
@ -245,7 +259,7 @@ open class WebDav(
|
||||
webDavClient.newCallResponse {
|
||||
url(url)
|
||||
method("MKCOL", null)
|
||||
}.let {
|
||||
}.use {
|
||||
checkResult(it)
|
||||
}
|
||||
}
|
||||
@ -300,7 +314,7 @@ open class WebDav(
|
||||
webDavClient.newCallResponse {
|
||||
url(url)
|
||||
put(fileBody)
|
||||
}.let {
|
||||
}.use {
|
||||
checkResult(it)
|
||||
}
|
||||
}
|
||||
@ -320,7 +334,7 @@ open class WebDav(
|
||||
webDavClient.newCallResponse {
|
||||
url(url)
|
||||
put(fileBody)
|
||||
}.let {
|
||||
}.use {
|
||||
checkResult(it)
|
||||
}
|
||||
}
|
||||
@ -340,7 +354,7 @@ open class WebDav(
|
||||
webDavClient.newCallResponse {
|
||||
url(url)
|
||||
put(fileBody)
|
||||
}.let {
|
||||
}.use {
|
||||
checkResult(it)
|
||||
}
|
||||
}
|
||||
@ -371,7 +385,7 @@ open class WebDav(
|
||||
webDavClient.newCallResponse {
|
||||
url(url)
|
||||
method("DELETE", null)
|
||||
}.let {
|
||||
}.use {
|
||||
checkResult(it)
|
||||
}
|
||||
}.onFailure {
|
||||
|
@ -21,6 +21,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
import kotlin.math.min
|
||||
|
||||
@ -51,6 +52,9 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
/* web端阅读进度记录 */
|
||||
var webBookProgress: BookProgress? = null
|
||||
|
||||
var preDownloadTask: Coroutine<*>? = null
|
||||
val downloadedChapters = hashSetOf<Int>()
|
||||
|
||||
//暂时保存跳转前进度
|
||||
fun saveCurrentBookProcess() {
|
||||
if (lastBookPress != null) return //避免进度条连续跳转不能覆盖最初的进度记录
|
||||
@ -319,7 +323,7 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
/**
|
||||
* 下载正文
|
||||
*/
|
||||
private fun downloadIndex(index: Int) {
|
||||
private suspend fun downloadIndex(index: Int) {
|
||||
if (index < 0) return
|
||||
if (index > chapterSize - 1) {
|
||||
upToc()
|
||||
@ -331,7 +335,9 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->
|
||||
if (BookHelp.hasContent(book, chapter)) {
|
||||
removeLoading(chapter.index)
|
||||
downloadedChapters.add(chapter.index)
|
||||
} else {
|
||||
delay(1000)
|
||||
download(this, chapter, false)
|
||||
}
|
||||
} ?: removeLoading(index)
|
||||
@ -482,17 +488,22 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
if (AppConfig.preDownloadNum < 2) {
|
||||
return
|
||||
}
|
||||
Coroutine.async {
|
||||
preDownloadTask?.cancel()
|
||||
preDownloadTask = Coroutine.async {
|
||||
//预下载
|
||||
val maxChapterIndex = durChapterIndex + AppConfig.preDownloadNum
|
||||
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
|
||||
delay(1000)
|
||||
downloadIndex(i)
|
||||
launch {
|
||||
val maxChapterIndex = min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
|
||||
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
|
||||
if (downloadedChapters.contains(i)) continue
|
||||
downloadIndex(i)
|
||||
}
|
||||
}
|
||||
val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
|
||||
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
|
||||
delay(1000)
|
||||
downloadIndex(i)
|
||||
launch {
|
||||
val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
|
||||
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
|
||||
if (downloadedChapters.contains(i)) continue
|
||||
downloadIndex(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
|
||||
|
||||
mText.isUri() -> {
|
||||
val uri = Uri.parse(mText)
|
||||
uri.inputStream(context).getOrThrow().let {
|
||||
uri.inputStream(context).getOrThrow().use {
|
||||
allSources.addAll(GSON.fromJsonArray<BookSource>(it).getOrThrow())
|
||||
}
|
||||
}
|
||||
@ -166,7 +166,7 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
|
||||
} else {
|
||||
url(url)
|
||||
}
|
||||
}.byteStream().let {
|
||||
}.byteStream().use {
|
||||
allSources.addAll(GSON.fromJsonArray<BookSource>(it).getOrThrow())
|
||||
}
|
||||
}
|
||||
|
@ -1220,30 +1220,27 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
val previousResult = binding.searchMenu.previousSearchResult
|
||||
|
||||
fun jumpToPosition() {
|
||||
ReadBook.curTextChapter?.let {
|
||||
binding.searchMenu.updateSearchInfo()
|
||||
val (pageIndex, lineIndex, charIndex, addLine, charIndex2) =
|
||||
viewModel.searchResultPositions(it, searchResult)
|
||||
ReadBook.skipToPage(pageIndex) {
|
||||
launch {
|
||||
isSelectingSearchResult = true
|
||||
binding.readView.curPage.selectStartMoveIndex(0, lineIndex, charIndex)
|
||||
when (addLine) {
|
||||
0 -> binding.readView.curPage.selectEndMoveIndex(
|
||||
0,
|
||||
lineIndex,
|
||||
charIndex + viewModel.searchContentQuery.length - 1
|
||||
)
|
||||
1 -> binding.readView.curPage.selectEndMoveIndex(
|
||||
0, lineIndex + 1, charIndex2
|
||||
)
|
||||
//consider change page, jump to scroll position
|
||||
-1 -> binding.readView.curPage.selectEndMoveIndex(1, 0, charIndex2)
|
||||
}
|
||||
binding.readView.isTextSelected = true
|
||||
isSelectingSearchResult = false
|
||||
}
|
||||
val curTextChapter = ReadBook.curTextChapter ?: return
|
||||
binding.searchMenu.updateSearchInfo()
|
||||
val (pageIndex, lineIndex, charIndex, addLine, charIndex2) =
|
||||
viewModel.searchResultPositions(curTextChapter, searchResult)
|
||||
ReadBook.skipToPage(pageIndex) {
|
||||
isSelectingSearchResult = true
|
||||
binding.readView.curPage.selectStartMoveIndex(0, lineIndex, charIndex)
|
||||
when (addLine) {
|
||||
0 -> binding.readView.curPage.selectEndMoveIndex(
|
||||
0,
|
||||
lineIndex,
|
||||
charIndex + viewModel.searchContentQuery.length - 1
|
||||
)
|
||||
1 -> binding.readView.curPage.selectEndMoveIndex(
|
||||
0, lineIndex + 1, charIndex2
|
||||
)
|
||||
//consider change page, jump to scroll position
|
||||
-1 -> binding.readView.curPage.selectEndMoveIndex(1, 0, charIndex2)
|
||||
}
|
||||
binding.readView.isTextSelected = true
|
||||
isSelectingSearchResult = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1281,13 +1278,11 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
|
||||
private fun startBackupJob() {
|
||||
backupJob?.cancel()
|
||||
backupJob = launch {
|
||||
backupJob = launch(IO) {
|
||||
delay(300000)
|
||||
withContext(IO) {
|
||||
ReadBook.book?.let {
|
||||
AppWebDav.uploadBookProgress(it)
|
||||
Backup.autoBack(this@ReadBookActivity)
|
||||
}
|
||||
ReadBook.book?.let {
|
||||
AppWebDav.uploadBookProgress(it)
|
||||
Backup.autoBack(this@ReadBookActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1323,6 +1318,8 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
if (ReadBook.callBack === this) {
|
||||
ReadBook.callBack = null
|
||||
}
|
||||
ReadBook.preDownloadTask?.cancel()
|
||||
ReadBook.downloadedChapters.clear()
|
||||
if (!BuildConfig.DEBUG) {
|
||||
Backup.autoBack(this)
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package io.legado.app.ui.book.toc
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import io.legado.app.R
|
||||
@ -19,7 +21,6 @@ import io.legado.app.utils.gone
|
||||
import io.legado.app.utils.longToastOnUi
|
||||
import io.legado.app.utils.visible
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class ChapterListAdapter(context: Context, val callback: Callback) :
|
||||
@ -27,6 +28,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
|
||||
|
||||
val cacheFileNames = hashSetOf<String>()
|
||||
private val displayTitleMap = ConcurrentHashMap<String, String>()
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
override val diffItemCallback: DiffUtil.ItemCallback<BookChapter>
|
||||
get() = object : DiffUtil.ItemCallback<BookChapter>() {
|
||||
@ -80,7 +82,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
|
||||
val displayTitle = item.getDisplayTitle(replaceRules, useReplace)
|
||||
ensureActive()
|
||||
displayTitleMap[item.title] = displayTitle
|
||||
withContext(Main) {
|
||||
handler.post {
|
||||
notifyItemChanged(i, true)
|
||||
}
|
||||
}
|
||||
@ -94,7 +96,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
|
||||
val displayTitle = item.getDisplayTitle(replaceRules, useReplace)
|
||||
ensureActive()
|
||||
displayTitleMap[item.title] = displayTitle
|
||||
withContext(Main) {
|
||||
handler.post {
|
||||
notifyItemChanged(i, true)
|
||||
}
|
||||
}
|
||||
|
@ -158,6 +158,7 @@ class BackupConfigFragment : PreferenceFragment(),
|
||||
showHelp()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.menu_log -> showDialogFragment<AppLogDialog>()
|
||||
}
|
||||
return false
|
||||
@ -174,6 +175,7 @@ class BackupConfigFragment : PreferenceFragment(),
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
context ?: return
|
||||
when (key) {
|
||||
PreferKey.backupPath -> upPreferenceSummary(key, getPrefString(key))
|
||||
PreferKey.webDavUrl,
|
||||
@ -183,6 +185,7 @@ class BackupConfigFragment : PreferenceFragment(),
|
||||
upPreferenceSummary(key, getPrefString(key))
|
||||
viewModel.upWebDavConfig()
|
||||
}
|
||||
|
||||
PreferKey.webDavDeviceName -> upPreferenceSummary(key, getPrefString(key))
|
||||
}
|
||||
}
|
||||
@ -196,22 +199,26 @@ class BackupConfigFragment : PreferenceFragment(),
|
||||
} else {
|
||||
preference.summary = value.toString()
|
||||
}
|
||||
|
||||
PreferKey.webDavAccount ->
|
||||
if (value.isNullOrBlank()) {
|
||||
preference.summary = getString(R.string.web_dav_account_s)
|
||||
} else {
|
||||
preference.summary = value.toString()
|
||||
}
|
||||
|
||||
PreferKey.webDavPassword ->
|
||||
if (value.isNullOrBlank()) {
|
||||
preference.summary = getString(R.string.web_dav_pw_s)
|
||||
} else {
|
||||
preference.summary = "*".repeat(value.toString().length)
|
||||
}
|
||||
|
||||
PreferKey.webDavDir -> preference.summary = when (value) {
|
||||
null -> "legado"
|
||||
else -> value
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (preference is ListPreference) {
|
||||
val index = preference.findIndexOfValue(value)
|
||||
|
2
modules/web/src/components.d.ts
vendored
2
modules/web/src/components.d.ts
vendored
@ -22,14 +22,12 @@ declare module '@vue/runtime-core' {
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||
ElLink: typeof import('element-plus/es')['ElLink']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElText: typeof import('element-plus/es')['ElText']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
PopCatalog: typeof import('./components/PopCatalog.vue')['default']
|
||||
ReadSettings: typeof import('./components/ReadSettings.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
@ -27,7 +27,7 @@ const store = useSourceStore();
|
||||
const printDebug = ref("");
|
||||
const searchKey = ref("");
|
||||
|
||||
watchEffect(() => {
|
||||
watch(() => store.isDebuging, () => {
|
||||
if (store.isDebuging) startDebug();
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user