Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -19,6 +19,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 链式协程
|
* 链式协程
|
||||||
|
* 注意:如果协程太快完成,回调会不执行
|
||||||
*/
|
*/
|
||||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||||
class Coroutine<T>(
|
class Coroutine<T>(
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import io.legado.app.utils.postEvent
|
|||||||
|
|
||||||
import io.legado.app.utils.startService
|
import io.legado.app.utils.startService
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
@@ -268,6 +269,7 @@ object CacheBook {
|
|||||||
book,
|
book,
|
||||||
chapter,
|
chapter,
|
||||||
context = context,
|
context = context,
|
||||||
|
start = CoroutineStart.LAZY,
|
||||||
executeContext = context
|
executeContext = context
|
||||||
).onSuccess { content ->
|
).onSuccess { content ->
|
||||||
onSuccess(chapter)
|
onSuccess(chapter)
|
||||||
@@ -282,7 +284,7 @@ object CacheBook {
|
|||||||
onCancel(chapterIndex)
|
onCancel(chapterIndex)
|
||||||
}.onFinally {
|
}.onFinally {
|
||||||
onFinally()
|
onFinally()
|
||||||
}
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -297,22 +299,28 @@ object CacheBook {
|
|||||||
postEvent(EventBus.UP_DOWNLOAD, book.bookUrl)
|
postEvent(EventBus.UP_DOWNLOAD, book.bookUrl)
|
||||||
onDownloadSet.add(chapter.index)
|
onDownloadSet.add(chapter.index)
|
||||||
waitDownloadSet.remove(chapter.index)
|
waitDownloadSet.remove(chapter.index)
|
||||||
WebBook.getContent(scope, bookSource, book, chapter, executeContext = IO)
|
WebBook.getContent(
|
||||||
.onSuccess { content ->
|
scope,
|
||||||
onSuccess(chapter)
|
bookSource,
|
||||||
ReadBook.downloadedChapters.add(chapter.index)
|
book,
|
||||||
ReadBook.downloadFailChapters.remove(chapter.index)
|
chapter,
|
||||||
downloadFinish(chapter, content, resetPageOffset)
|
start = CoroutineStart.LAZY,
|
||||||
}.onError {
|
executeContext = IO
|
||||||
onError(chapter, it)
|
).onSuccess { content ->
|
||||||
ReadBook.downloadFailChapters[chapter.index] =
|
onSuccess(chapter)
|
||||||
(ReadBook.downloadFailChapters[chapter.index] ?: 0) + 1
|
ReadBook.downloadedChapters.add(chapter.index)
|
||||||
downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}", resetPageOffset)
|
ReadBook.downloadFailChapters.remove(chapter.index)
|
||||||
}.onCancel {
|
downloadFinish(chapter, content, resetPageOffset)
|
||||||
onCancel(chapter.index)
|
}.onError {
|
||||||
}.onFinally {
|
onError(chapter, it)
|
||||||
postEvent(EventBus.UP_DOWNLOAD, book.bookUrl)
|
ReadBook.downloadFailChapters[chapter.index] =
|
||||||
}
|
(ReadBook.downloadFailChapters[chapter.index] ?: 0) + 1
|
||||||
|
downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}", resetPageOffset)
|
||||||
|
}.onCancel {
|
||||||
|
onCancel(chapter.index)
|
||||||
|
}.onFinally {
|
||||||
|
postEvent(EventBus.UP_DOWNLOAD, book.bookUrl)
|
||||||
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadFinish(
|
private fun downloadFinish(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import io.legado.app.help.coroutine.CompositeCoroutine
|
|||||||
import io.legado.app.ui.book.search.SearchScope
|
import io.legado.app.ui.book.search.SearchScope
|
||||||
import io.legado.app.utils.getPrefBoolean
|
import io.legado.app.utils.getPrefBoolean
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.ExecutorCoroutineDispatcher
|
import kotlinx.coroutines.ExecutorCoroutineDispatcher
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
@@ -76,7 +77,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
searchIndex++
|
searchIndex++
|
||||||
val source = bookSourceList[searchIndex]
|
val source = bookSourceList.getOrNull(searchIndex) ?: return
|
||||||
val searchPool = searchPool ?: return
|
val searchPool = searchPool ?: return
|
||||||
val task = WebBook.searchBook(
|
val task = WebBook.searchBook(
|
||||||
scope,
|
scope,
|
||||||
@@ -84,6 +85,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB
|
|||||||
searchKey,
|
searchKey,
|
||||||
searchPage,
|
searchPage,
|
||||||
context = searchPool,
|
context = searchPool,
|
||||||
|
start = CoroutineStart.LAZY,
|
||||||
executeContext = searchPool
|
executeContext = searchPool
|
||||||
).timeout(30000L)
|
).timeout(30000L)
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
@@ -93,6 +95,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB
|
|||||||
.onFinally {
|
.onFinally {
|
||||||
onFinally(searchId)
|
onFinally(searchId)
|
||||||
}
|
}
|
||||||
|
task.start()
|
||||||
tasks.add(task)
|
tasks.add(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import io.legado.app.model.analyzeRule.AnalyzeRule
|
|||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
import io.legado.app.model.analyzeRule.RuleData
|
import io.legado.app.model.analyzeRule.RuleData
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
@@ -30,9 +31,10 @@ object WebBook {
|
|||||||
key: String,
|
key: String,
|
||||||
page: Int? = 1,
|
page: Int? = 1,
|
||||||
context: CoroutineContext = Dispatchers.IO,
|
context: CoroutineContext = Dispatchers.IO,
|
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||||
executeContext: CoroutineContext = Dispatchers.Main,
|
executeContext: CoroutineContext = Dispatchers.Main,
|
||||||
): Coroutine<ArrayList<SearchBook>> {
|
): Coroutine<ArrayList<SearchBook>> {
|
||||||
return Coroutine.async(scope, context, executeContext = executeContext) {
|
return Coroutine.async(scope, context, start = start, executeContext = executeContext) {
|
||||||
searchBookAwait(bookSource, key, page)
|
searchBookAwait(bookSource, key, page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,9 +268,10 @@ object WebBook {
|
|||||||
nextChapterUrl: String? = null,
|
nextChapterUrl: String? = null,
|
||||||
needSave: Boolean = true,
|
needSave: Boolean = true,
|
||||||
context: CoroutineContext = Dispatchers.IO,
|
context: CoroutineContext = Dispatchers.IO,
|
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||||
executeContext: CoroutineContext = Dispatchers.Main,
|
executeContext: CoroutineContext = Dispatchers.Main,
|
||||||
): Coroutine<String> {
|
): Coroutine<String> {
|
||||||
return Coroutine.async(scope, context, executeContext = executeContext) {
|
return Coroutine.async(scope, context, start = start, executeContext = executeContext) {
|
||||||
getContentAwait(bookSource, book, bookChapter, nextChapterUrl, needSave)
|
getContentAwait(bookSource, book, bookChapter, nextChapterUrl, needSave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ class AudioPlayService : BaseService(),
|
|||||||
.setBufferedPosition(exoPlayer.bufferedPosition)
|
.setBufferedPosition(exoPlayer.bufferedPosition)
|
||||||
.addCustomAction(
|
.addCustomAction(
|
||||||
APP_ACTION_STOP,
|
APP_ACTION_STOP,
|
||||||
getString(R.string.set_timer),
|
getString(R.string.stop),
|
||||||
R.drawable.ic_stop_black_24dp
|
R.drawable.ic_stop_black_24dp
|
||||||
)
|
)
|
||||||
.addCustomAction(
|
.addCustomAction(
|
||||||
|
|||||||
@@ -200,9 +200,13 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
|
|||||||
}
|
}
|
||||||
++searchIndex
|
++searchIndex
|
||||||
}
|
}
|
||||||
val source = bookSourceList[searchIndex]
|
val source = bookSourceList.getOrNull(searchIndex) ?: return
|
||||||
bookSourceList[searchIndex] = emptyBookSource
|
bookSourceList[searchIndex] = emptyBookSource
|
||||||
val task = execute(context = searchPool!!, executeContext = searchPool!!) {
|
val task = execute(
|
||||||
|
context = searchPool!!,
|
||||||
|
start = CoroutineStart.LAZY,
|
||||||
|
executeContext = searchPool!!
|
||||||
|
) {
|
||||||
val resultBooks = WebBook.searchBookAwait(source, name)
|
val resultBooks = WebBook.searchBookAwait(source, name)
|
||||||
resultBooks.forEach { searchBook ->
|
resultBooks.forEach { searchBook ->
|
||||||
if (searchBook.name != name) {
|
if (searchBook.name != name) {
|
||||||
@@ -223,11 +227,14 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
|
|||||||
}
|
}
|
||||||
}.timeout(60000L)
|
}.timeout(60000L)
|
||||||
.onError {
|
.onError {
|
||||||
|
ensureActive()
|
||||||
nextSearch()
|
nextSearch()
|
||||||
}
|
}
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
|
ensureActive()
|
||||||
nextSearch()
|
nextSearch()
|
||||||
}
|
}
|
||||||
|
task.start()
|
||||||
tasks.add(task)
|
tasks.add(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,8 +300,9 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
|
|||||||
searchCallback?.searchSuccess(searchBook)
|
searchCallback?.searchSuccess(searchBook)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
private fun nextSearch() {
|
private fun nextSearch() {
|
||||||
synchronized(this) {
|
kotlin.runCatching {
|
||||||
if (searchIndex < bookSourceList.lastIndex) {
|
if (searchIndex < bookSourceList.lastIndex) {
|
||||||
search()
|
search()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import org.apache.commons.text.StringEscapeUtils
|
|||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
|
import java.util.regex.PatternSyntaxException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* rss阅读界面
|
* rss阅读界面
|
||||||
@@ -346,24 +347,32 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
|
|||||||
request: WebResourceRequest
|
request: WebResourceRequest
|
||||||
): WebResourceResponse? {
|
): WebResourceResponse? {
|
||||||
val url = request.url.toString()
|
val url = request.url.toString()
|
||||||
viewModel.rssSource?.let { source ->
|
val source = viewModel.rssSource ?: return super.shouldInterceptRequest(view, request)
|
||||||
val blacklist = source.contentBlacklist?.splitNotBlank(",")
|
val blacklist = source.contentBlacklist?.splitNotBlank(",")
|
||||||
if (!blacklist.isNullOrEmpty()) {
|
if (!blacklist.isNullOrEmpty()) {
|
||||||
blacklist.forEach {
|
blacklist.forEach {
|
||||||
|
try {
|
||||||
if (url.startsWith(it) || url.matches(it.toRegex())) {
|
if (url.startsWith(it) || url.matches(it.toRegex())) {
|
||||||
return createEmptyResource()
|
return createEmptyResource()
|
||||||
}
|
}
|
||||||
|
} catch (e: PatternSyntaxException) {
|
||||||
|
AppLog.put("黑名单规则正则语法错误 源名称:${source.sourceName} 正则:$it", e)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
val whitelist = source.contentWhitelist?.splitNotBlank(",")
|
} else {
|
||||||
if (!whitelist.isNullOrEmpty()) {
|
val whitelist = source.contentWhitelist?.splitNotBlank(",")
|
||||||
whitelist.forEach {
|
if (!whitelist.isNullOrEmpty()) {
|
||||||
|
whitelist.forEach {
|
||||||
|
try {
|
||||||
if (url.startsWith(it) || url.matches(it.toRegex())) {
|
if (url.startsWith(it) || url.matches(it.toRegex())) {
|
||||||
return super.shouldInterceptRequest(view, request)
|
return super.shouldInterceptRequest(view, request)
|
||||||
}
|
}
|
||||||
|
} catch (e: PatternSyntaxException) {
|
||||||
|
val msg = "白名单规则正则语法错误 源名称:${source.sourceName} 正则:$it"
|
||||||
|
AppLog.put(msg, e)
|
||||||
}
|
}
|
||||||
return createEmptyResource()
|
|
||||||
}
|
}
|
||||||
|
return createEmptyResource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.shouldInterceptRequest(view, request)
|
return super.shouldInterceptRequest(view, request)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package io.legado.app.utils
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
@@ -28,12 +29,10 @@ fun Context.toastOnUi(message: Int, duration: Int = Toast.LENGTH_SHORT) {
|
|||||||
fun Context.toastOnUi(message: CharSequence?, duration: Int = Toast.LENGTH_SHORT) {
|
fun Context.toastOnUi(message: CharSequence?, duration: Int = Toast.LENGTH_SHORT) {
|
||||||
runOnUI {
|
runOnUI {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
if (toast == null || BuildConfig.DEBUG || AppConfig.recordLog) {
|
toast?.cancel()
|
||||||
toast?.cancel()
|
toast = Toast(this)
|
||||||
toast = Toast(this)
|
val toastView: View = inflate(R.layout.view_toast)
|
||||||
toast?.view = inflate(R.layout.view_toast)
|
toast?.view = toastView
|
||||||
}
|
|
||||||
val toastView = toast?.view!!
|
|
||||||
val cardView = toastView.findViewById<CardView>(R.id.cv_content)
|
val cardView = toastView.findViewById<CardView>(R.id.cv_content)
|
||||||
cardView.setCardBackgroundColor(bottomBackground)
|
cardView.setCardBackgroundColor(bottomBackground)
|
||||||
val isLight = ColorUtils.isColorLight(bottomBackground)
|
val isLight = ColorUtils.isColorLight(bottomBackground)
|
||||||
|
|||||||
Reference in New Issue
Block a user