Merge remote-tracking branch 'origin/master'

This commit is contained in:
kunfei
2023-05-09 08:11:09 +08:00
19 changed files with 2100 additions and 118 deletions
+2 -2
View File
@@ -149,7 +149,7 @@ dependencies {
//noinspection GradleDependency,DifferentStdlibGradleVersion
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
//协程
def coroutines_version = '1.6.4'
def coroutines_version = '1.7.0'
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version")
@@ -210,7 +210,7 @@ dependencies {
//网络
implementation('com.squareup.okhttp3:okhttp:4.11.0')
appImplementation(fileTree(dir: 'cronetlib', include: ['*.jar', '*.aar']))
appImplementation 'com.google.protobuf:protobuf-javalite:3.22.3'
appImplementation 'com.google.protobuf:protobuf-javalite:3.22.4'
//Glide
def glideVersion = "4.15.1"
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
{"x86_64":"0dc99aac60adf1a59f87c27dcc3110d6","x86":"4792bb318202d8e34a2f9a4da69ee372","armeabi-v7a":"72c6a274cf45ac90eabf0aec303a8ed5","arm64-v8a":"fde78ffa83893ea3112918b97b85a0e7","version":"113.0.5672.61"}
{"x86_64":"8fa0f4f53877dd089e49bcbaaac95108","x86":"6f030a93e7995bfd1675208ee8d83af5","armeabi-v7a":"d61a5169ad4ad0a99fa5d1f3c9702e9a","arm64-v8a":"dad33527a4038a9f455c7ef0dd3e2af6","version":"113.0.5672.76"}
+11 -1
View File
@@ -12,9 +12,19 @@
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
**2023/05/08**
* 更新cronet: 113.0.5672.76
* web: 全新传书页面 [MakeHui](https://github.com/MakeHui);修正源编辑列表操作逻辑和多选性能
* 优化部分文本提示toast
* webView: 忽略部分网站的证书错误
* 备份恢复webDav: 输入密码后自动验证
* 换源添加加载字数选项
* 修复全文搜索开始bug
* 其中一些更新由 Xwite, Horis, Jason Yao提供
**2023/05/03**
* 更新cronet: 113.0.5672.61
* 文件类书源文件后缀识别:自动识别失败后会读取下载链接UrlOption的type字段
```
@@ -19,9 +19,11 @@ object SourceHelp {
private val list18Plus by lazy {
try {
return@lazy String(appCtx.assets.open("18PlusList.txt").readBytes())
.splitNotBlank("\n")
.splitNotBlank("\n").map {
EncoderUtils.base64Decode(it)
}.toHashSet()
} catch (e: Exception) {
return@lazy arrayOf<String>()
return@lazy hashSetOf<String>()
}
}
@@ -32,42 +34,42 @@ object SourceHelp {
}
fun insertRssSource(vararg rssSources: RssSource) {
rssSources.forEach { rssSource ->
if (is18Plus(rssSource.sourceUrl)) {
handler.post {
appCtx.toastOnUi("${rssSource.sourceName}是18+网址,禁止导入.")
}
} else {
appDb.rssSourceDao.insert(rssSource)
val rssSourcesGroup = rssSources.groupBy {
is18Plus(it.sourceUrl)
}
rssSourcesGroup[true]?.forEach {
handler.post {
appCtx.toastOnUi("${it.sourceName}是18+网址,禁止导入.")
}
}
rssSourcesGroup[false]?.let {
appDb.rssSourceDao.insert(*it.toTypedArray())
}
}
fun insertBookSource(vararg bookSources: BookSource) {
bookSources.forEach { bookSource ->
if (is18Plus(bookSource.bookSourceUrl)) {
handler.post {
appCtx.toastOnUi("${bookSource.bookSourceName}是18+网址,禁止导入.")
}
} else {
appDb.bookSourceDao.insert(bookSource)
val bookSourcesGroup = bookSources.groupBy {
is18Plus(it.bookSourceUrl)
}
bookSourcesGroup[true]?.forEach {
handler.post {
appCtx.toastOnUi("${it.bookSourceName}是18+网址,禁止导入.")
}
}
bookSourcesGroup[false]?.let {
appDb.bookSourceDao.insert(*it.toTypedArray())
}
}
private fun is18Plus(url: String?): Boolean {
url ?: return false
val baseUrl = NetworkUtils.getBaseUrl(url)
baseUrl ?: return false
if (AppConst.isPlayChannel) return false
val baseUrl = NetworkUtils.getBaseUrl(url) ?: return false
kotlin.runCatching {
val host = baseUrl.split("//", ".")
val base64Url = EncoderUtils.base64Encode("${host[host.lastIndex - 1]}.${host.last()}")
list18Plus.forEach {
if (base64Url == it) {
return true
}
val host = baseUrl.split("//", ".").let {
if (it.size > 2) "${it[it.lastIndex - 1]}.${it.last()}" else return false
}
return list18Plus.contains(host)
}
return false
}
@@ -307,7 +307,6 @@ object ReadBook : CoroutineScope by MainScope() {
contentLoadFinish(book, chapter, it, upContent, resetPageOffset) {
success?.invoke()
}
removeLoading(chapter.index)
} ?: download(this, chapter, resetPageOffset = resetPageOffset)
} ?: removeLoading(index)
}.onError {
@@ -320,26 +319,24 @@ object ReadBook : CoroutineScope by MainScope() {
/**
* 下载正文
*/
private fun download(index: Int) {
private fun downloadIndex(index: Int) {
if (index < 0) return
if (index > chapterSize - 1) {
upToc()
return
}
book?.let { book ->
if (book.isLocal) return
if (addLoading(index)) {
Coroutine.async {
appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->
if (BookHelp.hasContent(book, chapter)) {
removeLoading(chapter.index)
} else {
download(this, chapter, false)
}
} ?: removeLoading(index)
}.onError {
removeLoading(index)
}
val book = book ?: return
if (addLoading(index)) {
try {
appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->
if (BookHelp.hasContent(book, chapter)) {
removeLoading(chapter.index)
} else {
download(this, chapter, false)
}
} ?: removeLoading(index)
} catch (e: Exception) {
removeLoading(index)
}
}
}
@@ -353,20 +350,17 @@ object ReadBook : CoroutineScope by MainScope() {
resetPageOffset: Boolean,
success: (() -> Unit)? = null
) {
val book = book
val book = book ?: return removeLoading(chapter.index)
val bookSource = bookSource
if (book != null && bookSource != null) {
if (bookSource != null) {
CacheBook.getOrCreate(bookSource, book).download(scope, chapter)
} else if (book != null) {
} else {
val msg = if (book.isLocal) "无内容" else "没有书源"
contentLoadFinish(
book, chapter, "加载正文失败\n$msg", resetPageOffset = resetPageOffset
) {
success?.invoke()
}
removeLoading(chapter.index)
} else {
removeLoading(chapter.index)
}
}
@@ -415,10 +409,12 @@ object ReadBook : CoroutineScope by MainScope() {
curPageChanged()
callBack?.contentLoadFinish()
}
-1 -> {
prevTextChapter = textChapter
if (upContent) callBack?.upContent(offset, resetPageOffset)
}
1 -> {
nextTextChapter = textChapter
if (upContent) callBack?.upContent(offset, resetPageOffset)
@@ -464,18 +460,17 @@ object ReadBook : CoroutineScope by MainScope() {
fun saveRead() {
Coroutine.async {
book?.let { book ->
book.lastCheckCount = 0
book.durChapterTime = System.currentTimeMillis()
book.durChapterIndex = durChapterIndex
book.durChapterPos = durChapterPos
appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
book.durChapterTitle = it.getDisplayTitle(
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
)
}
appDb.bookDao.update(book)
val book = book ?: return@async
book.lastCheckCount = 0
book.durChapterTime = System.currentTimeMillis()
book.durChapterIndex = durChapterIndex
book.durChapterPos = durChapterPos
appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
book.durChapterTitle = it.getDisplayTitle(
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
)
}
appDb.bookDao.update(book)
}
}
@@ -483,6 +478,7 @@ object ReadBook : CoroutineScope by MainScope() {
* 预下载
*/
private fun preDownload() {
if (book?.isLocal == true) return
if (AppConfig.preDownloadNum < 2) {
return
}
@@ -491,12 +487,12 @@ object ReadBook : CoroutineScope by MainScope() {
val maxChapterIndex = durChapterIndex + AppConfig.preDownloadNum
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
delay(1000)
download(i)
downloadIndex(i)
}
val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
delay(1000)
download(i)
downloadIndex(i)
}
}
}
@@ -430,7 +430,7 @@ class AudioPlayService : BaseService(),
private fun upMediaSessionPlaybackState(state: Int) {
mediaSessionCompat?.setPlaybackState(
PlaybackStateCompat.Builder()
.setActions(MediaHelp.MEDIA_SESSION_ACTIONS)
.setActions(PlaybackStateCompat.ACTION_SEEK_TO)
.setState(state, exoPlayer.currentPosition, 1f)
.setBufferedPosition(exoPlayer.bufferedPosition)
.build()
@@ -27,6 +27,7 @@ import io.legado.app.databinding.DialogBookChangeSourceBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.ui.widget.dialog.WaitDialog
@@ -86,7 +87,7 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
binding.toolBar.setBackgroundColor(primaryColor)
viewModel.initData(arguments)
viewModel.initData(arguments, callBack?.oldBook, activity is ReadBookActivity)
showTitle()
initMenu()
initRecyclerView()
@@ -212,19 +213,23 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_
item.isChecked = !item.isChecked
viewModel.refresh()
}
R.id.menu_load_info -> {
AppConfig.changeSourceLoadInfo = !item.isChecked
item.isChecked = !item.isChecked
}
R.id.menu_load_toc -> {
AppConfig.changeSourceLoadToc = !item.isChecked
item.isChecked = !item.isChecked
}
R.id.menu_load_word_count -> {
AppConfig.changeSourceLoadWordCount = !item.isChecked
item.isChecked = !item.isChecked
viewModel.onLoadWordCountChecked(item.isChecked)
}
R.id.menu_start_stop -> viewModel.startOrStopSearch()
R.id.menu_source_manage -> startActivity<BookSourceActivity>()
R.id.menu_refresh_list -> viewModel.startRefreshList()
@@ -306,7 +311,7 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_
}
override fun setBookScore(searchBook: SearchBook, score: Int) {
viewModel.setBookScore(searchBook,score)
viewModel.setBookScore(searchBook, score)
}
override fun getBookScore(searchBook: SearchBook): Int {
@@ -16,12 +16,14 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.book.BookHelp
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.SourceConfig
import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.ReadBook
import io.legado.app.model.webBook.WebBook
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
@@ -44,6 +46,8 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
var searchFinishCallback: ((isEmpty: Boolean) -> Unit)? = null
var name: String = ""
var author: String = ""
private var fromReadBookActivity = false
private var oldBook: Book? = null
private var tasks = CompositeCoroutine()
private var screenKey: String = ""
private var bookSourceList = arrayListOf<BookSource>()
@@ -114,7 +118,7 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
}
@CallSuper
open fun initData(arguments: Bundle?) {
open fun initData(arguments: Bundle?, book: Book?, fromReadBookActivity: Boolean) {
arguments?.let { bundle ->
bundle.getString("name")?.let {
name = it
@@ -122,6 +126,8 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
bundle.getString("author")?.let {
author = it.replace(AppPattern.authorRegex, "")
}
this.fromReadBookActivity = fromReadBookActivity
oldBook = book
}
}
@@ -180,22 +186,24 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
val task = Coroutine.async(scope = viewModelScope, context = searchPool!!) {
val resultBooks = WebBook.searchBookAwait(source, name)
resultBooks.forEach { searchBook ->
if (searchBook.name == name) {
if ((AppConfig.changeSourceCheckAuthor && searchBook.author.contains(author))
|| !AppConfig.changeSourceCheckAuthor
) {
when {
searchBook.latestChapterTitle.isNullOrEmpty() &&
(AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) -> {
loadBookInfo(source, searchBook.toBook())
}
searchBook.chapterWordCountText.isNullOrBlank() && AppConfig.changeSourceLoadWordCount -> {
loadBookToc(source, searchBook.toBook())
}
else -> {
searchCallback?.searchSuccess(searchBook)
}
}
if (searchBook.name != name) {
return@forEach
}
if (AppConfig.changeSourceCheckAuthor && !searchBook.author.contains(author)) {
return@forEach
}
when {
searchBook.latestChapterTitle.isNullOrEmpty() &&
(AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) -> {
loadBookInfo(source, searchBook.toBook())
}
AppConfig.changeSourceLoadWordCount -> {
loadBookToc(source, searchBook.toBook())
}
else -> {
searchCallback?.searchSuccess(searchBook)
}
}
}
@@ -211,7 +219,7 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
private suspend fun loadBookInfo(source: BookSource, book: Book) {
WebBook.getBookInfoAwait(source, book)
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
if (AppConfig.changeSourceLoadToc || AppConfig.changeSourceLoadWordCount) {
loadBookToc(source, book)
} else {
//从详情页里获取最新章节
@@ -224,7 +232,7 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
val chapters = WebBook.getChapterListAwait(source, book).getOrThrow()
tocMap[book.bookUrl] = chapters
bookMap[book.bookUrl] = book
if (context.getPrefBoolean(PreferKey.changeSourceLoadWordCount)) {
if (AppConfig.changeSourceLoadWordCount) {
loadBookWordCount(source, book, chapters)
} else {
val searchBook = book.toSearchBook()
@@ -237,16 +245,26 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
book: Book,
chapters: List<BookChapter>
) = coroutineScope {
val chapterIndex = ReadBook.curTextChapter?.chapter?.index ?: (chapters.size - 1)
val chapterIndex = if (fromReadBookActivity) {
oldBook?.let {
BookHelp.getDurChapter(
it.durChapterIndex,
it.durChapterTitle,
chapters,
it.totalChapterNum
)
} ?: chapters.lastIndex
} else chapters.lastIndex
val bookChapter = chapters.getOrNull(chapterIndex)
val startTime = System.currentTimeMillis()
val pair = try {
if (bookChapter == null) throw NoStackTraceException("章节缺失,总章节数${chapters.size}")
if (!isActive) return@coroutineScope
WebBook.getContentAwait(source, book, bookChapter, null, false).length.let {
val nextChapterUrl = chapters.getOrNull(chapterIndex + 1)?.url
WebBook.getContentAwait(source, book, bookChapter, nextChapterUrl, false).length.let {
it to "${chapterIndex + 1}章 字数:${it}"
}
} catch (t: Throwable) {
if (t is CancellationException) throw t
-1 to "${chapterIndex + 1}章 获取字数失败:${t.localizedMessage}"
}
val endTime = System.currentTimeMillis()
@@ -13,6 +13,7 @@ import io.legado.app.base.adapter.DiffRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemChangeSourceBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.gone
import io.legado.app.utils.invisible
@@ -55,6 +56,8 @@ class ChangeChapterSourceAdapter(
tvOrigin.text = item.originName
tvAuthor.text = item.author
tvLast.text = item.getDisplayLastChapterTitle()
tvCurrentChapterWordCount.text = item.chapterWordCountText
tvRespondTime.text = context.getString(R.string.respondTime, item.respondTime)
if (callBack.oldBookUrl == item.bookUrl) {
ivChecked.visible()
} else {
@@ -82,13 +85,37 @@ class ChangeChapterSourceAdapter(
} else if (score < 0) {
binding.ivGood.gone()
binding.ivBad.visible()
DrawableCompat.setTint(binding.ivGood.drawable, appCtx.getCompatColor(R.color.md_red_100))
DrawableCompat.setTint(binding.ivBad.drawable, appCtx.getCompatColor(R.color.md_blue_A200))
DrawableCompat.setTint(
binding.ivGood.drawable,
appCtx.getCompatColor(R.color.md_red_100)
)
DrawableCompat.setTint(
binding.ivBad.drawable,
appCtx.getCompatColor(R.color.md_blue_A200)
)
} else {
binding.ivGood.visible()
binding.ivBad.visible()
DrawableCompat.setTint(binding.ivGood.drawable, appCtx.getCompatColor(R.color.md_red_100))
DrawableCompat.setTint(binding.ivBad.drawable, appCtx.getCompatColor(R.color.md_blue_100))
DrawableCompat.setTint(
binding.ivGood.drawable,
appCtx.getCompatColor(R.color.md_red_100)
)
DrawableCompat.setTint(
binding.ivBad.drawable,
appCtx.getCompatColor(R.color.md_blue_100)
)
}
if (AppConfig.changeSourceLoadWordCount && !item.chapterWordCountText.isNullOrBlank()) {
tvCurrentChapterWordCount.visible()
} else {
tvCurrentChapterWordCount.gone()
}
if (AppConfig.changeSourceLoadWordCount && item.respondTime >= 0) {
tvRespondTime.visible()
} else {
tvRespondTime.gone()
}
}
}
@@ -2,16 +2,20 @@ package io.legado.app.ui.book.changesource
import android.content.DialogInterface
import android.os.Bundle
import android.view.*
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_UP
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
@@ -28,11 +32,21 @@ import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.elevation
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.*
import io.legado.app.utils.StartActivityContract
import io.legado.app.utils.applyTint
import io.legado.app.utils.cnCompare
import io.legado.app.utils.dpToPx
import io.legado.app.utils.gone
import io.legado.app.utils.observeEvent
import io.legado.app.utils.setLayout
import io.legado.app.utils.startActivity
import io.legado.app.utils.toastOnUi
import io.legado.app.utils.viewbindingdelegate.viewBinding
import io.legado.app.utils.visible
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.launch
@@ -99,7 +113,7 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
binding.toolBar.setBackgroundColor(primaryColor)
viewModel.initData(arguments)
viewModel.initData(arguments, callBack?.oldBook, activity is ReadBookActivity)
showTitle()
initMenu()
initView()
@@ -124,6 +138,8 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c
?.isChecked = AppConfig.changeSourceLoadInfo
binding.toolBar.menu.findItem(R.id.menu_load_toc)
?.isChecked = AppConfig.changeSourceLoadToc
binding.toolBar.menu.findItem(R.id.menu_load_word_count)
?.isChecked = AppConfig.changeSourceLoadWordCount
}
private fun initView() {
@@ -239,6 +255,11 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c
AppConfig.changeSourceLoadToc = !item.isChecked
item.isChecked = !item.isChecked
}
R.id.menu_load_word_count -> {
AppConfig.changeSourceLoadWordCount = !item.isChecked
item.isChecked = !item.isChecked
viewModel.onLoadWordCountChecked(item.isChecked)
}
R.id.menu_start_stop -> viewModel.startOrStopSearch()
R.id.menu_source_manage -> startActivity<BookSourceActivity>()
else -> if (item?.groupId == R.id.source_group) {
@@ -15,8 +15,8 @@ class ChangeChapterSourceViewModel(application: Application) :
var chapterIndex: Int = 0
var chapterTitle: String = ""
override fun initData(arguments: Bundle?) {
super.initData(arguments)
override fun initData(arguments: Bundle?, book: Book?, fromReadBookActivity: Boolean) {
super.initData(arguments, book, fromReadBookActivity)
arguments?.let { bundle ->
bundle.getString("chapterTitle")?.let {
chapterTitle = it
+2 -2
View File
@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx3000m
org.gradle.jvmargs=-Xmx6000m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
@@ -39,7 +39,7 @@ android.defaults.buildfeatures.shaders=false
# and none from the library's dependencies, thereby reducing the size of the R class for that library.
android.nonTransitiveRClass=true
# https://chromiumdash.appspot.com/releases?platform=Android
CronetVersion=113.0.5672.61
CronetVersion=113.0.5672.76
CronetMainVersion=113.0.0.0
android.injected.testOnly=false
android.nonFinalResIds=true
+19 -1
View File
@@ -1,4 +1,5 @@
<template>
<div class="title">{{ title }}</div>
<div v-for="(para, index) in carray" :key="index">
<img
class="full"
@@ -16,7 +17,11 @@ import config from "../plugins/config";
import { getImageFromLegado, isLegadoUrl } from "../plugins/utils";
const store = useBookStore();
defineProps(["carray"]);
const props = defineProps({
carray: { type: Array, required: true },
title: { type: String, required: true },
spacing: { type: Object, required: true },
});
const fontFamily = computed(() => {
if (store.config.font >= 0) {
@@ -49,14 +54,27 @@ watch(fontSize, () => {
store.setShowContent(true);
});
});
const letterSpacing = computed(() => props.spacing.letter); //字间距 倍数
const lineSpacing = computed(() => 1 + props.spacing.line); //行距 基础行距1
const paragraphSpacing = computed(() => props.spacing.paragraph); //段距
</script>
<style lang="scss" scoped>
.title {
margin-bottom: 57px;
font: 24px / 32px PingFangSC-Regular, HelveticaNeue-Light,
"Helvetica Neue Light", "Microsoft YaHei", sans-serif;
}
p {
display: block;
word-wrap: break-word;
word-break: break-all;
letter-spacing: calc(v-bind("letterSpacing") * 1em);
line-height: v-bind("lineSpacing");
margin: calc(v-bind("paragraphSpacing") * 1em) 0;
:deep(img) {
height: 1em;
}
+6 -1
View File
@@ -22,6 +22,11 @@ export const useBookStore = defineStore("book", {
readWidth: 800,
infiniteLoading: false,
customFontName: "",
spacing: {
paragraph: 1,
line: 0.8,
letter: 0,
},
},
miniInterface: false,
readSettingsVisible: false,
@@ -53,7 +58,7 @@ export const useBookStore = defineStore("book", {
this.readingBook = readingBook;
},
setConfig(config) {
this.config = config;
Object.assign(this.config, config);
},
setReadSettingsVisible(visible) {
this.readSettingsVisible = visible;
+11 -6
View File
@@ -22,15 +22,19 @@ export const useSourceStore = defineStore("source", {
},
getters: {
sources: (state) => (isBookSource ? state.bookSources : state.rssSources),
sourceUrlKey: () => isBookSource ? "bookSourceUrl" : "sourceUrl",
sourceUrlKey: () => (isBookSource ? "bookSourceUrl" : "sourceUrl"),
sourcesMap: (state) => {
let map = new Map();
state.sources.forEach(source => map.set(source[state.sourceUrlKey], source));
state.sources.forEach((source) =>
map.set(source[state.sourceUrlKey], source)
);
return map;
},
savedSourcesMap: (state) => {
let map = new Map();
state.savedSources.forEach(source => map.set(source[state.sourceUrlKey], source));
state.savedSources.forEach((source) =>
map.set(source[state.sourceUrlKey], source)
);
return map;
},
currentSourceUrl: (state) =>
@@ -73,13 +77,14 @@ export const useSourceStore = defineStore("source", {
},
//保存当前编辑源
saveCurrentSource() {
let source = this.currentSource, map = this.sourcesMap;
let source = this.currentSource,
map = this.sourcesMap;
map.set(source[this.sourceUrlKey], source);
this.saveSources(Array.from(map.values()));
},
// 更改当前编辑的源
changeCurrentSource(source) {
this.currentSource = JSON.parse(JSON.stringify((source)));
this.currentSource = JSON.parse(JSON.stringify(source));
},
// update editTab tabName and editTab info
changeTabName(tabName) {
@@ -87,7 +92,7 @@ export const useSourceStore = defineStore("source", {
localStorage.setItem("tabName", tabName);
},
changeEditTabSource(source) {
this.editTabSource = JSON.parse(JSON.stringify((source)));
this.editTabSource = JSON.parse(JSON.stringify(source));
},
editHistory(history) {
let historyObj;
+6 -10
View File
@@ -82,10 +82,12 @@
<div class="content">
<div class="top-bar" ref="top"></div>
<div v-for="data in chapterData" :key="data.index" ref="chapter">
<div class="title" :index="data.index" v-if="showContent">
{{ data.title }}
</div>
<chapter-content :carray="data.content" v-if="showContent" />
<chapter-content
:carray="data.content"
:title="data.title"
:spacing="store.config.spacing"
v-if="showContent"
/>
</div>
<div class="loading" ref="loading"></div>
<div class="bottom-bar" ref="bottom"></div>
@@ -667,12 +669,6 @@ onUnmounted(() => {
font-family: "Microsoft YaHei", PingFangSC-Regular, HelveticaNeue-Light,
"Helvetica Neue Light", sans-serif;
.title {
margin-bottom: 57px;
font: 24px / 32px PingFangSC-Regular, HelveticaNeue-Light,
"Helvetica Neue Light", "Microsoft YaHei", sans-serif;
}
.bottom-bar,
.top-bar {
height: 64px;