Merge branch 'gedoor:master' into master

pull/1604/head
Xwite 3 years ago committed by GitHub
commit 60ec2e2d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      app/build.gradle
  2. 3
      app/src/main/assets/updateLog.md
  3. 24
      app/src/main/java/io/legado/app/data/entities/Book.kt
  4. 3
      app/src/main/java/io/legado/app/data/entities/BookChapter.kt
  5. 3
      app/src/main/java/io/legado/app/model/ReadAloud.kt
  6. 8
      app/src/main/java/io/legado/app/model/ReadBook.kt
  7. 3
      app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
  8. 2
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  9. 1
      app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt
  10. 61
      app/src/main/java/io/legado/app/ui/book/toc/ChapterListAdapter.kt
  11. 28
      app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt
  12. 10
      app/src/main/res/layout/fragment_chapter_list.xml

@ -192,8 +192,9 @@ dependencies {
implementation(fileTree(dir: 'cronetlib', include: ['*.jar', '*.aar'])) implementation(fileTree(dir: 'cronetlib', include: ['*.jar', '*.aar']))
//Glide //Glide
implementation('com.github.bumptech.glide:glide:4.12.0') def glideVersion = "4.13.0"
kapt('com.github.bumptech.glide:compiler:4.12.0') implementation("com.github.bumptech.glide:glide:$glideVersion")
kapt("com.github.bumptech.glide:compiler:$glideVersion")
//webServer //webServer
def nanoHttpdVersion = "2.3.1" def nanoHttpdVersion = "2.3.1"

@ -11,13 +11,14 @@
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
**2022/02/08** **2022/02/09**
* 校验失效分组具体到搜索发现目录正文 by Xwite * 校验失效分组具体到搜索发现目录正文 by Xwite
* txt文件初次解析目录不选择禁用的正则 * txt文件初次解析目录不选择禁用的正则
* txt单章字数超102400均分txt,添加开关 by Xwite * txt单章字数超102400均分txt,添加开关 by Xwite
* 修复tts被回收后无法继续朗读的bug,重新初始化tts * 修复tts被回收后无法继续朗读的bug,重新初始化tts
* webDav备份支持自定义文件夹 * webDav备份支持自定义文件夹
* 其它一些优化
**2022/02/03** **2022/02/03**

@ -6,6 +6,7 @@ import io.legado.app.constant.AppPattern
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.MD5Utils import io.legado.app.utils.MD5Utils
@ -149,7 +150,7 @@ data class Book(
} }
fun getUseReplaceRule(): Boolean { fun getUseReplaceRule(): Boolean {
return config().useReplaceRule return config().useReplaceRule ?: AppConfig.replaceEnableDefault
} }
fun getReSegment(): Boolean { fun getReSegment(): Boolean {
@ -169,10 +170,14 @@ data class Book(
} }
fun getPageAnim(): Int { fun getPageAnim(): Int {
return config().pageAnim var pageAnim = config().pageAnim ?: ReadBookConfig.pageAnim
if (pageAnim < 0) {
pageAnim = ReadBookConfig.pageAnim
}
return pageAnim
} }
fun setPageAnim(pageAnim: Int) { fun setPageAnim(pageAnim: Int?) {
config().pageAnim = pageAnim config().pageAnim = pageAnim
} }
@ -184,6 +189,14 @@ data class Book(
config().imageStyle = imageStyle config().imageStyle = imageStyle
} }
fun setTtsEngine(ttsEngine: String?) {
config().ttsEngine = ttsEngine
}
fun getTtsEngine(): String? {
return config().ttsEngine
}
fun getDelTag(tag: Long): Boolean { fun getDelTag(tag: Long): Boolean {
return config().delTag and tag == tag return config().delTag and tag == tag
} }
@ -276,12 +289,13 @@ data class Book(
@Parcelize @Parcelize
data class ReadConfig( data class ReadConfig(
var reverseToc: Boolean = false, var reverseToc: Boolean = false,
var pageAnim: Int = -1, var pageAnim: Int? = null,
var reSegment: Boolean = false, var reSegment: Boolean = false,
var limitContentLength: Boolean = true, //txt规则解析目录时超过规定的最大字数时均分txt var limitContentLength: Boolean = true, //txt规则解析目录时超过规定的最大字数时均分txt
var imageStyle: String? = null, var imageStyle: String? = null,
var useReplaceRule: Boolean = AppConfig.replaceEnableDefault,// 正文使用净化替换规则 var useReplaceRule: Boolean? = null,// 正文使用净化替换规则
var delTag: Long = 0L,//去除标签 var delTag: Long = 0L,//去除标签
var ttsEngine: String? = null,
) : Parcelable ) : Parcelable
class Converters { class Converters {

@ -16,7 +16,6 @@ import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import splitties.init.appCtx import splitties.init.appCtx
@Suppress("unused")
@Parcelize @Parcelize
@Entity( @Entity(
tableName = "chapters", tableName = "chapters",
@ -124,8 +123,10 @@ data class BookChapter(
} }
} }
@Suppress("unused")
fun getFileName(): String = String.format("%05d-%s.nb", index, MD5Utils.md5Encode16(title)) fun getFileName(): String = String.format("%05d-%s.nb", index, MD5Utils.md5Encode16(title))
@Suppress("unused")
fun getFontName(): String = String.format("%05d-%s.ttf", index, MD5Utils.md5Encode16(title)) fun getFontName(): String = String.format("%05d-%s.ttf", index, MD5Utils.md5Encode16(title))
} }

@ -14,10 +14,11 @@ import splitties.init.appCtx
object ReadAloud { object ReadAloud {
private var aloudClass: Class<*> = getReadAloudClass() private var aloudClass: Class<*> = getReadAloudClass()
val ttsEngine get() = ReadBook.book?.getTtsEngine() ?: AppConfig.ttsEngine
var httpTTS: HttpTTS? = null var httpTTS: HttpTTS? = null
private fun getReadAloudClass(): Class<*> { private fun getReadAloudClass(): Class<*> {
val ttsEngine = AppConfig.ttsEngine val ttsEngine = ttsEngine
if (ttsEngine.isNullOrBlank()) { if (ttsEngine.isNullOrBlank()) {
return TTSReadAloudService::class.java return TTSReadAloudService::class.java
} }

@ -402,13 +402,7 @@ object ReadBook : CoroutineScope by MainScope() {
} }
fun pageAnim(): Int { fun pageAnim(): Int {
book?.let { return book?.getPageAnim() ?: ReadBookConfig.pageAnim
return if (it.getPageAnim() < 0)
ReadBookConfig.pageAnim
else
it.getPageAnim()
}
return ReadBookConfig.pageAnim
} }
fun setCharset(charset: String) { fun setCharset(charset: String) {

@ -12,6 +12,7 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.MediaHelp import io.legado.app.help.MediaHelp
import io.legado.app.lib.dialogs.SelectItem import io.legado.app.lib.dialogs.SelectItem
import io.legado.app.model.NoStackTraceException import io.legado.app.model.NoStackTraceException
import io.legado.app.model.ReadAloud
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.utils.* import io.legado.app.utils.*
import java.util.* import java.util.*
@ -36,7 +37,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
@Synchronized @Synchronized
private fun initTts() { private fun initTts() {
ttsInitFinish = false ttsInitFinish = false
val engine = GSON.fromJsonObject<SelectItem<String>>(AppConfig.ttsEngine)?.value val engine = GSON.fromJsonObject<SelectItem<String>>(ReadAloud.ttsEngine)?.value
textToSpeech = if (engine.isNullOrBlank()) { textToSpeech = if (engine.isNullOrBlank()) {
TextToSpeech(this, this) TextToSpeech(this, this)
} else { } else {

@ -126,7 +126,7 @@ class ReadStyleDialog : BaseDialogFragment(R.layout.dialog_read_book_style),
TipConfigDialog().show(childFragmentManager, "tipConfigDialog") TipConfigDialog().show(childFragmentManager, "tipConfigDialog")
} }
rgPageAnim.setOnCheckedChangeListener { _, checkedId -> rgPageAnim.setOnCheckedChangeListener { _, checkedId ->
ReadBook.book?.setPageAnim(-1) ReadBook.book?.setPageAnim(null)
ReadBookConfig.pageAnim = binding.rgPageAnim.getIndexById(checkedId) ReadBookConfig.pageAnim = binding.rgPageAnim.getIndexById(checkedId)
callBack?.upPageAnim() callBack?.upPageAnim()
ReadBook.loadContent(false) ReadBook.loadContent(false)

@ -17,7 +17,6 @@ import io.legado.app.utils.setEdgeEffectColor
import io.legado.app.utils.showDialogFragment import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

@ -6,39 +6,41 @@ import androidx.recyclerview.widget.DiffUtil
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.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.databinding.ItemChapterListBinding import io.legado.app.databinding.ItemChapterListBinding
import io.legado.app.help.ContentProcessor
import io.legado.app.lib.theme.ThemeUtils import io.legado.app.lib.theme.ThemeUtils
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.getCompatColor import io.legado.app.utils.getCompatColor
import io.legado.app.utils.gone import io.legado.app.utils.gone
import io.legado.app.utils.visible import io.legado.app.utils.visible
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.launch
class ChapterListAdapter(context: Context, val callback: Callback) : class ChapterListAdapter(context: Context, val callback: Callback, private val scope: CoroutineScope) :
RecyclerAdapter<BookChapter, ItemChapterListBinding>(context) { RecyclerAdapter<Pair<BookChapter, Deferred<String>>, ItemChapterListBinding>(context) {
val replaceRules
get() = callback.book?.let {
ContentProcessor.get(it.name, it.origin).getReplaceRules()
}
val useReplace get() = callback.book?.getUseReplaceRule() == true
val cacheFileNames = hashSetOf<String>() val cacheFileNames = hashSetOf<String>()
val diffCallBack = object : DiffUtil.ItemCallback<BookChapter>() { val diffCallBack = object : DiffUtil.ItemCallback<Pair<BookChapter, Deferred<String>>>() {
override fun areItemsTheSame(oldItem: BookChapter, newItem: BookChapter): Boolean { override fun areItemsTheSame(
return oldItem.index == newItem.index oldItem: Pair<BookChapter, Deferred<String>>,
newItem: Pair<BookChapter, Deferred<String>>
): Boolean {
return oldItem.first.index == newItem.first.index
} }
override fun areContentsTheSame(oldItem: BookChapter, newItem: BookChapter): Boolean { override fun areContentsTheSame(
return oldItem.bookUrl == newItem.bookUrl oldItem: Pair<BookChapter, Deferred<String>>,
&& oldItem.url == newItem.url newItem: Pair<BookChapter, Deferred<String>>
&& oldItem.isVip == newItem.isVip ): Boolean {
&& oldItem.isPay == newItem.isPay return oldItem.first.bookUrl == newItem.first.bookUrl
&& oldItem.title == newItem.title && oldItem.first.url == newItem.first.url
&& oldItem.tag == newItem.tag && oldItem.first.isVip == newItem.first.isVip
&& oldItem.isVolume == newItem.isVolume && oldItem.first.isPay == newItem.first.isPay
&& oldItem.first.title == newItem.first.title
&& oldItem.first.tag == newItem.first.tag
&& oldItem.first.isVolume == newItem.first.isVolume
} }
} }
@ -50,20 +52,22 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
override fun convert( override fun convert(
holder: ItemViewHolder, holder: ItemViewHolder,
binding: ItemChapterListBinding, binding: ItemChapterListBinding,
item: BookChapter, item: Pair<BookChapter, Deferred<String>>,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
binding.run { binding.run {
val isDur = callback.durChapterIndex() == item.index val isDur = callback.durChapterIndex() == item.first.index
val cached = callback.isLocalBook || cacheFileNames.contains(item.getFileName()) val cached = callback.isLocalBook || cacheFileNames.contains(item.first.getFileName())
if (payloads.isEmpty()) { if (payloads.isEmpty()) {
if (isDur) { if (isDur) {
tvChapterName.setTextColor(context.accentColor) tvChapterName.setTextColor(context.accentColor)
} else { } else {
tvChapterName.setTextColor(context.getCompatColor(R.color.primaryText)) tvChapterName.setTextColor(context.getCompatColor(R.color.primaryText))
} }
tvChapterName.text = item.getDisplayTitle(replaceRules, useReplace) scope.launch {
if (item.isVolume) { tvChapterName.text = item.second.await()
}
if (item.first.isVolume) {
//卷名,如第一卷 突出显示 //卷名,如第一卷 突出显示
tvChapterItem.setBackgroundColor(context.getCompatColor(R.color.btn_bg_press)) tvChapterItem.setBackgroundColor(context.getCompatColor(R.color.btn_bg_press))
} else { } else {
@ -71,9 +75,9 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
tvChapterItem.background = tvChapterItem.background =
ThemeUtils.resolveDrawable(context, android.R.attr.selectableItemBackground) ThemeUtils.resolveDrawable(context, android.R.attr.selectableItemBackground)
} }
if (!item.tag.isNullOrEmpty() && !item.isVolume) { if (!item.first.tag.isNullOrEmpty() && !item.first.isVolume) {
//卷名不显示tag(更新时间规则) //卷名不显示tag(更新时间规则)
tvTag.text = item.tag tvTag.text = item.first.tag
tvTag.visible() tvTag.visible()
} else { } else {
tvTag.gone() tvTag.gone()
@ -88,7 +92,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
override fun registerListener(holder: ItemViewHolder, binding: ItemChapterListBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemChapterListBinding) {
holder.itemView.setOnClickListener { holder.itemView.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
callback.openChapter(it) callback.openChapter(it.first)
} }
} }
} }
@ -104,7 +108,6 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
} }
interface Callback { interface Callback {
val book: Book?
val isLocalBook: Boolean val isLocalBook: Boolean
fun openChapter(bookChapter: BookChapter) fun openChapter(bookChapter: BookChapter)
fun durChapterIndex(): Int fun durChapterIndex(): Int

@ -14,6 +14,7 @@ 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.databinding.FragmentChapterListBinding import io.legado.app.databinding.FragmentChapterListBinding
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.lib.theme.getPrimaryTextColor
import io.legado.app.ui.widget.recycler.UpLinearLayoutManager import io.legado.app.ui.widget.recycler.UpLinearLayoutManager
@ -21,12 +22,9 @@ import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.ColorUtils import io.legado.app.utils.ColorUtils
import io.legado.app.utils.observeEvent import io.legado.app.utils.observeEvent
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapter_list), class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapter_list),
ChapterListAdapter.Callback, ChapterListAdapter.Callback,
@ -34,7 +32,7 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
override val viewModel by activityViewModels<TocViewModel>() override val viewModel by activityViewModels<TocViewModel>()
private val binding by viewBinding(FragmentChapterListBinding::bind) private val binding by viewBinding(FragmentChapterListBinding::bind)
private val mLayoutManager by lazy { UpLinearLayoutManager(requireContext()) } private val mLayoutManager by lazy { UpLinearLayoutManager(requireContext()) }
private val adapter by lazy { ChapterListAdapter(requireContext(), this) } private val adapter by lazy { ChapterListAdapter(requireContext(), this, this) }
private var durChapterIndex = 0 private var durChapterIndex = 0
private var tocFlowJob: Job? = null private var tocFlowJob: Job? = null
@ -112,7 +110,22 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
else -> appDb.bookChapterDao.flowSearch(viewModel.bookUrl, searchKey) else -> appDb.bookChapterDao.flowSearch(viewModel.bookUrl, searchKey)
}.collect { }.collect {
if (!(searchKey.isNullOrBlank() && it.isEmpty())) { if (!(searchKey.isNullOrBlank() && it.isEmpty())) {
adapter.setItems(it, adapter.diffCallBack) binding.rotateLoading.show()
val data = withContext(IO) {
val replaces = viewModel.bookData.value?.let { book ->
ContentProcessor.get(book.name, book.origin).getReplaceRules()
}
val useReplace = viewModel.bookData.value?.getUseReplaceRule() == true
it.map { chapter ->
Pair(
chapter,
async(IO) {
chapter.getDisplayTitle(replaces, useReplace)
})
}
}
binding.rotateLoading.hide()
adapter.setItems(data, adapter.diffCallBack)
if (searchKey.isNullOrBlank() && mLayoutManager.findFirstVisibleItemPosition() < 0) { if (searchKey.isNullOrBlank() && mLayoutManager.findFirstVisibleItemPosition() < 0) {
mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0) mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0)
} }
@ -121,9 +134,6 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
} }
} }
override val book: Book?
get() = viewModel.bookData.value
override val isLocalBook: Boolean override val isLocalBook: Boolean
get() = viewModel.bookData.value?.isLocalBook() == true get() = viewModel.bookData.value?.isLocalBook() == true

@ -14,6 +14,16 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/ll_chapter_base_info" /> app:layout_constraintBottom_toTopOf="@+id/ll_chapter_base_info" />
<io.legado.app.ui.widget.anima.RotateLoading
android:id="@+id/rotate_loading"
android:layout_width="60dp"
android:layout_height="60dp"
android:visibility="gone"
app:layout_constraintTop_toTopOf="@id/recycler_view"
app:layout_constraintBottom_toBottomOf="@+id/recycler_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<LinearLayout <LinearLayout
android:id="@+id/ll_chapter_base_info" android:id="@+id/ll_chapter_base_info"
android:layout_width="match_parent" android:layout_width="match_parent"

Loading…
Cancel
Save