Merge remote-tracking branch 'upstream/master' into master

pull/341/head
AndyBernie 5 years ago
commit 5c562e3694
  1. 4
      app/src/main/assets/updateLog.md
  2. 2
      app/src/main/java/io/legado/app/constant/EventBus.kt
  3. 2
      app/src/main/java/io/legado/app/model/Debug.kt
  4. 20
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  5. 2
      app/src/main/java/io/legado/app/model/rss/Rss.kt
  6. 2
      app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt
  7. 7
      app/src/main/java/io/legado/app/model/webBook/WebBook.kt
  8. 2
      app/src/main/java/io/legado/app/service/DownloadService.kt
  9. 2
      app/src/main/java/io/legado/app/service/help/AudioPlay.kt
  10. 2
      app/src/main/java/io/legado/app/service/help/CheckSource.kt
  11. 2
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  12. 2
      app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
  13. 2
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
  14. 102
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  15. 2
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt
  16. 2
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  17. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  18. 2
      app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
  19. 2
      app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugModel.kt
  20. 5
      app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt
  21. 2
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  22. 73
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
  23. 2
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
  24. 2
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
  25. 2
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
  26. 4
      app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
  27. 2
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
  28. 2
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
  29. 5
      app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
  30. 2
      app/src/main/java/io/legado/app/web/controller/BookshelfController.kt
  31. 2
      app/src/main/java/io/legado/app/web/controller/SourceDebugWebSocket.kt
  32. 7
      app/src/main/res/layout/dialog_book_group_picker.xml
  33. 1
      app/src/main/res/layout/dialog_change_cover.xml
  34. 7
      app/src/main/res/layout/dialog_file_chooser.xml
  35. 7
      app/src/main/res/layout/dialog_font_select.xml
  36. 7
      app/src/main/res/layout/dialog_toc_regex.xml

@ -3,6 +3,10 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。 * 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
- 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 - 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/08/27**
* 修复主题bug
* 修复封面bug
**2020/08/26** **2020/08/26**
* js添加java.encodeURI(speakText),用于解决js编码时有~的语句朗读不出来 * js添加java.encodeURI(speakText),用于解决js编码时有~的语句朗读不出来
* 修复书名太长删除阅读记录按钮不显示的bug * 修复书名太长删除阅读记录按钮不显示的bug

@ -3,7 +3,7 @@ package io.legado.app.constant
object EventBus { object EventBus {
const val MEDIA_BUTTON = "mediaButton" const val MEDIA_BUTTON = "mediaButton"
const val RECREATE = "RECREATE" const val RECREATE = "RECREATE"
const val UP_BOOK = "sourceDebugLog" const val UP_BOOK = "upBookToc"
const val ALOUD_STATE = "aloud_state" const val ALOUD_STATE = "aloud_state"
const val TTS_PROGRESS = "ttsStart" const val TTS_PROGRESS = "ttsStart"
const val TTS_DS = "ttsDs" const val TTS_DS = "ttsDs"

@ -6,6 +6,8 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.RssArticle import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.help.coroutine.CompositeCoroutine import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.model.rss.Rss
import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.htmlFormat import io.legado.app.utils.htmlFormat
import io.legado.app.utils.isAbsUrl import io.legado.app.utils.isAbsUrl
import java.text.SimpleDateFormat import java.text.SimpleDateFormat

@ -3,6 +3,8 @@ package io.legado.app.model.analyzeRule
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.text.TextUtils import android.text.TextUtils
import androidx.annotation.Keep import androidx.annotation.Keep
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import io.legado.app.constant.AppConst.SCRIPT_ENGINE import io.legado.app.constant.AppConst.SCRIPT_ENGINE
import io.legado.app.constant.AppConst.UA_NAME import io.legado.app.constant.AppConst.UA_NAME
import io.legado.app.constant.AppConst.userAgent import io.legado.app.constant.AppConst.userAgent
@ -22,8 +24,6 @@ import java.net.URLEncoder
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
import javax.script.SimpleBindings import javax.script.SimpleBindings
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
/** /**
* Created by GKF on 2018/1/24. * Created by GKF on 2018/1/24.
@ -50,7 +50,7 @@ class AnalyzeUrl(
private var baseUrl: String = "" private var baseUrl: String = ""
lateinit var url: String lateinit var url: String
private set private set
var path: String? = null lateinit var urlHasQuery: String
private set private set
val headerMap = HashMap<String, String>() val headerMap = HashMap<String, String>()
private var queryStr: String? = null private var queryStr: String? = null
@ -59,10 +59,11 @@ class AnalyzeUrl(
private var body: String? = null private var body: String? = null
private var requestBody: RequestBody? = null private var requestBody: RequestBody? = null
private var method = RequestMethod.GET private var method = RequestMethod.GET
private val splitUrlRegex = Regex(",[^\\{]*")
init { init {
baseUrl?.let { baseUrl?.let {
this.baseUrl = it.split(",[^\\{]*".toRegex(), 1)[0] this.baseUrl = it.split(splitUrlRegex, 1)[0]
} }
headerMapF?.let { headerMap.putAll(it) } headerMapF?.let { headerMap.putAll(it) }
//替换参数 //替换参数
@ -172,8 +173,9 @@ class AnalyzeUrl(
* 处理URL * 处理URL
*/ */
private fun initUrl() { private fun initUrl() {
var urlArray = ruleUrl.split(",[^\\{]*".toRegex(), 2) var urlArray = ruleUrl.split(splitUrlRegex, 2)
url = urlArray[0] url = urlArray[0]
urlHasQuery = urlArray[0]
NetworkUtils.getBaseUrl(url)?.let { NetworkUtils.getBaseUrl(url)?.let {
baseUrl = it baseUrl = it
} }
@ -392,13 +394,13 @@ class AnalyzeUrl(
@Throws(Exception::class) @Throws(Exception::class)
fun getGlideUrl(): Any? { fun getGlideUrl(): Any? {
var glideUrl: Any = url var glideUrl: Any = urlHasQuery
if(headerMap.isNotEmpty()) { if(headerMap.isNotEmpty()) {
val Headers = LazyHeaders.Builder() val headers = LazyHeaders.Builder()
headerMap.forEach {(key, value) -> headerMap.forEach {(key, value) ->
Headers.addHeader(key, value) headers.addHeader(key, value)
} }
glideUrl = GlideUrl(url, Headers.build()) glideUrl = GlideUrl(urlHasQuery, headers.build())
} }
return glideUrl return glideUrl
} }

@ -1,4 +1,4 @@
package io.legado.app.model package io.legado.app.model.rss
import io.legado.app.data.entities.RssArticle import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource

@ -1,4 +1,4 @@
package io.legado.app.model package io.legado.app.model.webBook
import io.legado.app.App import io.legado.app.App
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook

@ -1,15 +1,12 @@
package io.legado.app.model package io.legado.app.model.webBook
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.webBook.BookChapterList
import io.legado.app.model.webBook.BookContent
import io.legado.app.model.webBook.BookInfo
import io.legado.app.model.webBook.BookList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext

@ -16,7 +16,7 @@ import io.legado.app.help.BookHelp
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import io.legado.app.help.coroutine.CompositeCoroutine import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.help.Download import io.legado.app.service.help.Download
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO

@ -6,7 +6,7 @@ import androidx.lifecycle.MutableLiveData
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status import io.legado.app.constant.Status
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.AudioPlayService import io.legado.app.service.AudioPlayService
object AudioPlay { object AudioPlay {

@ -7,7 +7,7 @@ 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.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.CheckSourceService import io.legado.app.service.CheckSourceService
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

@ -13,7 +13,7 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider

@ -8,7 +8,7 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.help.AudioPlay import io.legado.app.service.help.AudioPlay
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

@ -9,7 +9,7 @@ import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.Executors import java.util.concurrent.Executors

@ -10,30 +10,35 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.debug import org.jetbrains.anko.debug
import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.Executors import java.util.concurrent.Executors
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) { class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
private var searchPool = val threadCount = AppConfig.threadCount
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() private lateinit var searchPool: ExecutorCoroutineDispatcher
val handler = Handler() val handler = Handler()
val searchStateData = MutableLiveData<Boolean>() val searchStateData = MutableLiveData<Boolean>()
val searchBooksLiveData = MutableLiveData<List<SearchBook>>() val searchBooksLiveData = MutableLiveData<List<SearchBook>>()
var name: String = "" var name: String = ""
var author: String = "" var author: String = ""
private var task: Coroutine<*>? = null private var tasks = CompositeCoroutine()
private var screenKey: String = "" private var screenKey: String = ""
private val searchBooks = hashSetOf<SearchBook>() private var bookSourceList = arrayListOf<BookSource>()
private val searchBooks = CopyOnWriteArraySet<SearchBook>()
private var postTime = 0L private var postTime = 0L
private val sendRunnable = Runnable { upAdapter() } private val sendRunnable = Runnable { upAdapter() }
@Volatile
private var searchIndex = -1
fun initData(arguments: Bundle?) { fun initData(arguments: Bundle?) {
arguments?.let { bundle -> arguments?.let { bundle ->
@ -46,13 +51,17 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
} }
} }
private fun initSearchPool() {
searchPool = Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
}
fun loadDbSearchBook() { fun loadDbSearchBook() {
execute { execute {
App.db.searchBookDao().getByNameAuthorEnable(name, author).let { App.db.searchBookDao().getByNameAuthorEnable(name, author).let {
searchBooks.addAll(it) searchBooks.addAll(it)
if (it.size <= 1) { if (it.size <= 1) {
upAdapter() upAdapter()
search() startSearch()
} else { } else {
upAdapter() upAdapter()
} }
@ -83,38 +92,53 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
upAdapter() upAdapter()
} }
fun search() { private fun startSearch() {
task = execute { execute {
val bookSourceList = App.db.bookSourceDao().allEnabled bookSourceList.clear()
for (item in bookSourceList) { bookSourceList.addAll(App.db.bookSourceDao().allEnabled)
//task取消时自动取消 by (scope = this@execute) searchStateData.postValue(true)
WebBook(item).searchBook(name, scope = this@execute, context = searchPool) initSearchPool()
.timeout(30000L) for (i in 0 until threadCount) {
.onSuccess(IO) { search()
it.forEach { searchBook -> }
if (searchBook.name == name && searchBook.author == author) { }
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) { }
if (searchBook.tocUrl.isEmpty()) {
loadBookInfo(searchBook.toBook()) private fun search() {
} else { synchronized(this) {
loadChapter(searchBook.toBook()) searchIndex++
} val source = bookSourceList[searchIndex]
val task = WebBook(source).searchBook(name, scope = this, context = searchPool)
.timeout(60000L)
.onSuccess(IO) {
it.forEach { searchBook ->
if (searchBook.name == name && searchBook.author == author) {
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
if (searchBook.tocUrl.isEmpty()) {
loadBookInfo(searchBook.toBook())
} else { } else {
searchFinish(searchBook) loadChapter(searchBook.toBook())
} }
return@onSuccess } else {
searchFinish(searchBook)
} }
return@forEach
} }
} }
} }
}.onStart { .onFinally {
searchStateData.postValue(true) synchronized(this) {
}.onCancel { if (searchIndex < bookSourceList.lastIndex) {
searchStateData.postValue(false) search()
} } else {
searchIndex++
task?.invokeOnCompletion { }
searchStateData.postValue(false) if (searchIndex >= bookSourceList.lastIndex + threadCount) {
searchStateData.postValue(false)
}
}
}
tasks.add(task)
} }
} }
@ -166,10 +190,12 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
} }
fun stopSearch() { fun stopSearch() {
if (task?.isActive == true) { if (tasks.isEmpty) {
task?.cancel() startSearch()
} else { } else {
search() tasks.clear()
searchPool.close()
searchStateData.postValue(false)
} }
} }

@ -7,7 +7,7 @@ import io.legado.app.App
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
class ExploreShowViewModel(application: Application) : BaseViewModel(application) { class ExploreShowViewModel(application: Application) : BaseViewModel(application) {

@ -9,7 +9,7 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.model.localBook.LocalBook import io.legado.app.model.localBook.LocalBook
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO

@ -10,7 +10,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.model.localBook.LocalBook import io.legado.app.model.localBook.LocalBook
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadAloud

@ -8,7 +8,7 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.SearchKeyword import io.legado.app.data.entities.SearchKeyword
import io.legado.app.model.SearchBookModel import io.legado.app.model.webBook.SearchBookModel
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive

@ -4,7 +4,7 @@ import android.app.Application
import io.legado.app.App import io.legado.app.App
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.model.Debug import io.legado.app.model.Debug
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
class BookSourceDebugModel(application: Application) : BaseViewModel(application), class BookSourceDebugModel(application: Application) : BaseViewModel(application),
Debug.Callback { Debug.Callback {

@ -12,7 +12,7 @@ import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.Theme import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.filechooser.adapter.FileAdapter import io.legado.app.ui.filechooser.adapter.FileAdapter
import io.legado.app.ui.filechooser.adapter.PathAdapter import io.legado.app.ui.filechooser.adapter.PathAdapter
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
@ -93,6 +93,7 @@ class FileChooserDialog : DialogFragment(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
tool_bar.setBackgroundColor(primaryColor)
view.setBackgroundResource(R.color.background_card) view.setBackgroundResource(R.color.background_card)
arguments?.let { arguments?.let {
requestCode = it.getInt("requestCode") requestCode = it.getInt("requestCode")
@ -129,7 +130,7 @@ class FileChooserDialog : DialogFragment(),
tool_bar.menu.add(menuTitle) tool_bar.menu.add(menuTitle)
} }
} }
tool_bar.menu.applyTint(requireContext(), Theme.getTheme()) tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this) tool_bar.setOnMenuItemClickListener(this)
} }

@ -62,7 +62,7 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
//自动更新书籍 //自动更新书籍
if (AppConfig.autoRefreshBook) { if (AppConfig.autoRefreshBook) {
view_pager_main.postDelayed({ view_pager_main.postDelayed({
viewModel.upChapterList() viewModel.upAllBookToc()
}, 1000) }, 1000)
} }
view_pager_main.postDelayed({ view_pager_main.postDelayed({

@ -11,20 +11,25 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.DefaultValueHelp import io.legado.app.help.DefaultValueHelp
import io.legado.app.help.http.HttpHelper import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.Restore import io.legado.app.help.storage.Restore
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.delay import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.Executors import java.util.concurrent.Executors
class MainViewModel(application: Application) : BaseViewModel(application) { class MainViewModel(application: Application) : BaseViewModel(application) {
private var upTocPool = val threadCount = AppConfig.threadCount
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() private var upTocPool = Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
val updateList = hashSetOf<String>() val updateList = CopyOnWriteArraySet<String>()
private val bookMap = ConcurrentHashMap<String, Book>()
@Volatile
private var usePoolCount = 0
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
@ -36,48 +41,76 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
upTocPool = Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() upTocPool = Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
} }
fun upChapterList() { fun upAllBookToc() {
execute { execute {
upChapterList(App.db.bookDao().hasUpdateBooks) upToc(App.db.bookDao().hasUpdateBooks)
} }
} }
fun upChapterList(books: List<Book>) { fun upToc(books: List<Book>) {
execute { execute {
books.filter { books.filter {
it.origin != BookType.local && it.canUpdate it.origin != BookType.local && it.canUpdate
}.forEach { book -> }.forEach {
if (!updateList.contains(book.bookUrl)) { bookMap[it.bookUrl] = it
}
for (i in 0 until threadCount) {
if (usePoolCount < threadCount) {
usePoolCount++
updateToc()
}
}
}
}
private fun updateToc() {
synchronized(this) {
bookMap.forEach { bookEntry ->
if (!updateList.contains(bookEntry.key)) {
val book = bookEntry.value
synchronized(this) {
updateList.add(book.bookUrl)
postEvent(EventBus.UP_BOOK, book.bookUrl)
}
App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource -> App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource ->
synchronized(this) {
updateList.add(book.bookUrl)
postEvent(EventBus.UP_BOOK, book.bookUrl)
}
WebBook(bookSource).getChapterList(book, context = upTocPool) WebBook(bookSource).getChapterList(book, context = upTocPool)
.timeout(300000) .timeout(300000)
.onSuccess(IO) { .onSuccess(IO) {
synchronized(this) {
updateList.remove(book.bookUrl)
postEvent(EventBus.UP_BOOK, book.bookUrl)
}
App.db.bookDao().update(book) App.db.bookDao().update(book)
App.db.bookChapterDao().delByBook(book.bookUrl) App.db.bookChapterDao().delByBook(book.bookUrl)
App.db.bookChapterDao().insert(*it.toTypedArray()) App.db.bookChapterDao().insert(*it.toTypedArray())
} }
.onError { .onError {
it.printStackTrace()
}
.onFinally {
synchronized(this) { synchronized(this) {
bookMap.remove(bookEntry.key)
updateList.remove(book.bookUrl) updateList.remove(book.bookUrl)
postEvent(EventBus.UP_BOOK, book.bookUrl) postEvent(EventBus.UP_BOOK, book.bookUrl)
upNext()
} }
it.printStackTrace()
} }
} ?: synchronized(this) {
bookMap.remove(bookEntry.key)
updateList.remove(book.bookUrl)
postEvent(EventBus.UP_BOOK, book.bookUrl)
upNext()
} }
return@forEach
} }
delay(50)
} }
} }
} }
private fun upNext() {
if (bookMap.size > updateList.size) {
updateToc()
} else {
usePoolCount--
}
}
fun initRss() { fun initRss() {
execute { execute {
val url = "https://gitee.com/alanskycn/yuedu/raw/master/JS/RSS/rssSource" val url = "https://gitee.com/alanskycn/yuedu/raw/master/JS/RSS/rssSource"

@ -78,7 +78,7 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
val group = bookGroups[tab_layout.selectedTabPosition] val group = bookGroups[tab_layout.selectedTabPosition]
val fragment = fragmentMap[group.groupId] val fragment = fragmentMap[group.groupId]
fragment?.getBooks()?.let { fragment?.getBooks()?.let {
activityViewModel.upChapterList(it) activityViewModel.upToc(it)
} }
} }
R.id.menu_bookshelf_layout -> configBookshelf() R.id.menu_bookshelf_layout -> configBookshelf()

@ -7,7 +7,7 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO

@ -66,7 +66,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
refresh_layout.setColorSchemeColors(accentColor) refresh_layout.setColorSchemeColors(accentColor)
refresh_layout.setOnRefreshListener { refresh_layout.setOnRefreshListener {
refresh_layout.isRefreshing = false refresh_layout.isRefreshing = false
activityViewModel.upChapterList(booksAdapter.getItems()) activityViewModel.upToc(booksAdapter.getItems())
} }
val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout) val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout)
if (bookshelfLayout == 0) { if (bookshelfLayout == 0) {

@ -15,7 +15,7 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
@ -65,7 +65,7 @@ class ReplaceEditDialog : BaseDialogFragment(),
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground) tool_bar.setBackgroundColor(primaryColor)
mSoftKeyboardTool = KeyboardToolPop(requireContext(), AppConst.keyboardToolChars, this) mSoftKeyboardTool = KeyboardToolPop(requireContext(), AppConst.keyboardToolChars, this)
tool_bar.inflateMenu(R.menu.replace_edit) tool_bar.inflateMenu(R.menu.replace_edit)
tool_bar.menu.applyTint(requireContext()) tool_bar.menu.applyTint(requireContext())

@ -7,7 +7,7 @@ import io.legado.app.App
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.RssArticle import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.model.Rss import io.legado.app.model.rss.Rss
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

@ -17,7 +17,7 @@ import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.RssStar import io.legado.app.data.entities.RssStar
import io.legado.app.help.http.HttpHelper import io.legado.app.help.http.HttpHelper
import io.legado.app.model.Rss import io.legado.app.model.rss.Rss
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.DocumentUtils import io.legado.app.utils.DocumentUtils
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils

@ -22,7 +22,7 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.filechooser.FileChooserDialog import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.utils.* import io.legado.app.utils.*
@ -59,9 +59,10 @@ class FontSelectDialog : BaseDialogFragment(),
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(requireContext().bottomBackground) tool_bar.setBackgroundColor(primaryColor)
tool_bar.setTitle(R.string.select_font) tool_bar.setTitle(R.string.select_font)
tool_bar.inflateMenu(R.menu.font_select) tool_bar.inflateMenu(R.menu.font_select)
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this) tool_bar.setOnMenuItemClickListener(this)
adapter = FontAdapter(requireContext(), this) adapter = FontAdapter(requireContext(), this)
recycler_view.layoutManager = LinearLayoutManager(context) recycler_view.layoutManager = LinearLayoutManager(context)

@ -4,7 +4,7 @@ import io.legado.app.App
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject

@ -6,7 +6,7 @@ import fi.iki.elonen.NanoWSD
import io.legado.app.App import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.model.Debug import io.legado.app.model.Debug
import io.legado.app.model.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.isJson import io.legado.app.utils.isJson

@ -1,15 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar" android:id="@+id/tool_bar"
android:background="@color/background_menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"

@ -9,7 +9,6 @@
android:id="@+id/tool_bar" android:id="@+id/tool_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/background_menu"
android:elevation="5dp" android:elevation="5dp"
android:theme="?attr/actionBarStyle" android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle" app:titleTextAppearance="@style/ToolbarTitle"

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -7,9 +8,11 @@
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar" android:id="@+id/tool_bar"
android:background="@color/background_menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_path" android:id="@+id/rv_path"

@ -1,14 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar" android:id="@+id/tool_bar"
android:background="@color/background_menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"

@ -1,15 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar" android:id="@+id/tool_bar"
android:background="@color/background_menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"

Loading…
Cancel
Save