mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
{"arm64-v8a":"11ded68691faa98fb1ab552210c8d4a8","armeabi-v7a":"4a986b523de502fc85ba6ea51f6f7184","x86_64":"80a642112fe079f3d689a6420a27e926","x86":"e72bf103350222d212bcb0b641733d43","version":"105.0.5195.77"}
|
||||
{"arm64-v8a":"27f6651b39339e6734e7ca63a9c9022b","armeabi-v7a":"abbf91c8b8f3dd02a4bda8872e3b9b98","x86_64":"d6923e011add8b18a824ef5a7c85772e","x86":"ba27795b663a8420790da40d40b99f96","version":"105.0.5195.79"}
|
||||
@@ -11,10 +11,11 @@
|
||||
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
|
||||
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
||||
|
||||
**2022/09/03**
|
||||
**2022/09/07**
|
||||
|
||||
* 更新cronet: 105.0.5195.77
|
||||
* 更新cronet: 105.0.5195.79
|
||||
* 更新web端书源编辑 by jgckM
|
||||
* 修复仿真翻页点击时翻页动画异常,化仿真翻页点击翻页效果 by 821938089
|
||||
|
||||
**2022/08/31**
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ object BookController {
|
||||
}
|
||||
returnData.setData(content)
|
||||
} catch (e: Exception) {
|
||||
returnData.setErrorMsg(e.msg)
|
||||
returnData.setErrorMsg(e.stackTraceStr)
|
||||
}
|
||||
return returnData
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import io.legado.app.data.entities.ReplaceRule
|
||||
import io.legado.app.exception.RegexTimeoutException
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.utils.msg
|
||||
import io.legado.app.utils.replace
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import splitties.init.appCtx
|
||||
@@ -149,7 +149,7 @@ class ContentProcessor private constructor(
|
||||
} catch (e: RegexTimeoutException) {
|
||||
item.isEnabled = false
|
||||
appDb.replaceRuleDao.update(item)
|
||||
return item.name + e.msg
|
||||
return item.name + e.stackTraceStr
|
||||
} catch (e: CancellationException) {
|
||||
return mContent
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.legado.app.model.ReadAloud
|
||||
import io.legado.app.utils.FileUtils
|
||||
import io.legado.app.utils.getFile
|
||||
import io.legado.app.utils.longToastOnUi
|
||||
import io.legado.app.utils.msg
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
import splitties.init.appCtx
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
@@ -48,7 +48,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
||||
if (ex == null) return
|
||||
//保存日志文件
|
||||
saveCrashInfo2File(ex)
|
||||
context.longToastOnUi(ex.msg)
|
||||
context.longToastOnUi(ex.stackTraceStr)
|
||||
Thread.sleep(3000)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import splitties.init.appCtx
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.Charset
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -56,9 +57,8 @@ interface JsExtensions {
|
||||
analyzeUrl.getStrResponseAwait().body
|
||||
}.onFailure {
|
||||
AppLog.put("ajax(${urlStr}) error\n${it.localizedMessage}", it)
|
||||
it.printOnDebug()
|
||||
}.getOrElse {
|
||||
it.msg
|
||||
it.stackTraceStr
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,10 +91,9 @@ interface JsExtensions {
|
||||
kotlin.runCatching {
|
||||
analyzeUrl.getStrResponseAwait()
|
||||
}.onFailure {
|
||||
log("connect(${urlStr}) error\n${it.stackTraceToString()}")
|
||||
it.printOnDebug()
|
||||
AppLog.put("connect(${urlStr}) error\n${it.localizedMessage}", it)
|
||||
}.getOrElse {
|
||||
StrResponse(analyzeUrl.url, it.localizedMessage)
|
||||
StrResponse(analyzeUrl.url, it.stackTraceStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,10 +105,9 @@ interface JsExtensions {
|
||||
kotlin.runCatching {
|
||||
analyzeUrl.getStrResponseAwait()
|
||||
}.onFailure {
|
||||
log("ajax($urlStr,$header) error\n${it.stackTraceToString()}")
|
||||
it.printOnDebug()
|
||||
AppLog.put("ajax($urlStr,$header) error\n${it.localizedMessage}", it)
|
||||
}.getOrElse {
|
||||
StrResponse(analyzeUrl.url, it.localizedMessage)
|
||||
StrResponse(analyzeUrl.url, it.stackTraceStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +126,7 @@ interface JsExtensions {
|
||||
html = html,
|
||||
javaScript = js,
|
||||
headerMap = getSource()?.getHeaderMap(true),
|
||||
tag = getSource()?.getKey()
|
||||
tag = getSource()?.getKey()
|
||||
).getStrResponse().body
|
||||
}
|
||||
}
|
||||
@@ -146,15 +144,18 @@ interface JsExtensions {
|
||||
* 使用内置浏览器打开链接,并等待网页结果
|
||||
*/
|
||||
fun startBrowserAwait(url: String, title: String): StrResponse {
|
||||
return StrResponse(url, SourceVerificationHelp.getVerificationResult(getSource(), url, title, true))
|
||||
return StrResponse(
|
||||
url,
|
||||
SourceVerificationHelp.getVerificationResult(getSource(), url, title, true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开图片验证码对话框,等待返回验证结果
|
||||
*/
|
||||
fun getVerificationCode(imageUrl: String): String {
|
||||
return SourceVerificationHelp.getVerificationResult(getSource(), imageUrl, "", false)
|
||||
}
|
||||
fun getVerificationCode(imageUrl: String): String {
|
||||
return SourceVerificationHelp.getVerificationResult(getSource(), imageUrl, "", false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 可从网络,本地文件(阅读私有缓存目录和书籍保存位置支持相对路径)导入JavaScript脚本
|
||||
@@ -206,6 +207,29 @@ interface JsExtensions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param url 下载地址:可带参数type,文件后缀,不带默认zip
|
||||
* @return 下载的文件相对路径
|
||||
*/
|
||||
fun downloadFile(url: String): String {
|
||||
val analyzeUrl = AnalyzeUrl(url, source = getSource())
|
||||
val type = analyzeUrl.type ?: "zip"
|
||||
val path = FileUtils.getPath(
|
||||
FileUtils.createFolderIfNotExist(FileUtils.getCachePath()),
|
||||
"${MD5Utils.md5Encode16(url)}.${type}"
|
||||
)
|
||||
FileUtils.delete(path)
|
||||
analyzeUrl.getInputStream().use { iStream ->
|
||||
val file = FileUtils.createFileIfNotExist(path)
|
||||
FileOutputStream(file).use { oStream ->
|
||||
iStream.copyTo(oStream)
|
||||
}
|
||||
}
|
||||
return path.substring(FileUtils.getCachePath().length)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 实现16进制字符串转文件
|
||||
* @param content 需要转成文件的16进制字符串
|
||||
@@ -214,18 +238,18 @@ interface JsExtensions {
|
||||
*/
|
||||
fun downloadFile(content: String, url: String): String {
|
||||
val type = AnalyzeUrl(url, source = getSource()).type ?: return ""
|
||||
val zipPath = FileUtils.getPath(
|
||||
val path = FileUtils.getPath(
|
||||
FileUtils.createFolderIfNotExist(FileUtils.getCachePath()),
|
||||
"${MD5Utils.md5Encode16(url)}.${type}"
|
||||
)
|
||||
FileUtils.delete(zipPath)
|
||||
val zipFile = FileUtils.createFileIfNotExist(zipPath)
|
||||
FileUtils.delete(path)
|
||||
val file = FileUtils.createFileIfNotExist(path)
|
||||
StringUtils.hexStringToByte(content).let {
|
||||
if (it.isNotEmpty()) {
|
||||
zipFile.writeBytes(it)
|
||||
file.writeBytes(it)
|
||||
}
|
||||
}
|
||||
return zipPath.substring(FileUtils.getCachePath().length)
|
||||
return path.substring(FileUtils.getCachePath().length)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -436,13 +460,15 @@ interface JsExtensions {
|
||||
}
|
||||
|
||||
/**
|
||||
* js实现文件夹内所有文件读取
|
||||
* js实现文件夹内所有文本文件读取
|
||||
* @param path 文件夹相对路径
|
||||
* @return 所有文件字符串换行连接
|
||||
*/
|
||||
fun getTxtInFolder(unzipPath: String): String {
|
||||
if (unzipPath.isEmpty()) return ""
|
||||
val unzipFolder = getFile(unzipPath)
|
||||
fun getTxtInFolder(path: String): String {
|
||||
if (path.isEmpty()) return ""
|
||||
val folder = getFile(path)
|
||||
val contents = StringBuilder()
|
||||
unzipFolder.listFiles().let {
|
||||
folder.listFiles().let {
|
||||
if (it != null) {
|
||||
for (f in it) {
|
||||
val charsetName = EncodingDetect.getEncode(f)
|
||||
@@ -452,7 +478,7 @@ interface JsExtensions {
|
||||
contents.deleteCharAt(contents.length - 1)
|
||||
}
|
||||
}
|
||||
FileUtils.delete(unzipFolder.absolutePath)
|
||||
FileUtils.delete(folder.absolutePath)
|
||||
return contents.toString()
|
||||
}
|
||||
|
||||
@@ -836,7 +862,7 @@ interface JsExtensions {
|
||||
}
|
||||
|
||||
fun desBase64DecodeToString(
|
||||
data: String, key: String, transformation: String, iv: String
|
||||
data: String, key: String, transformation: String, iv: String
|
||||
): String? {
|
||||
return EncoderUtils.decryptBase64DES(
|
||||
data.encodeToByteArray(),
|
||||
@@ -1031,7 +1057,10 @@ interface JsExtensions {
|
||||
algorithm: String,
|
||||
key: String
|
||||
): String {
|
||||
return Base64.encodeToString(HMac(algorithm, key.toByteArray()).digest(data), Base64.NO_WRAP)
|
||||
return Base64.encodeToString(
|
||||
HMac(algorithm, key.toByteArray()).digest(data),
|
||||
Base64.NO_WRAP
|
||||
)
|
||||
}
|
||||
|
||||
fun md5Encode(str: String): String {
|
||||
|
||||
@@ -9,7 +9,7 @@ import io.legado.app.model.rss.Rss
|
||||
import io.legado.app.model.webBook.WebBook
|
||||
import io.legado.app.utils.HtmlFormatter
|
||||
import io.legado.app.utils.isAbsUrl
|
||||
import io.legado.app.utils.msg
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@@ -128,7 +128,7 @@ object Debug {
|
||||
}
|
||||
}
|
||||
.onError {
|
||||
log(debugSource, it.msg, state = -1)
|
||||
log(debugSource, it.stackTraceStr, state = -1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ object Debug {
|
||||
log(debugSource, "︽内容页解析完成", state = 1000)
|
||||
}
|
||||
.onError {
|
||||
log(debugSource, it.msg, state = -1)
|
||||
log(debugSource, it.stackTraceStr, state = -1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ object Debug {
|
||||
}
|
||||
}
|
||||
.onError {
|
||||
log(debugSource, it.msg, state = -1)
|
||||
log(debugSource, it.stackTraceStr, state = -1)
|
||||
}
|
||||
tasks.add(explore)
|
||||
}
|
||||
@@ -222,7 +222,7 @@ object Debug {
|
||||
}
|
||||
}
|
||||
.onError {
|
||||
log(debugSource, it.msg, state = -1)
|
||||
log(debugSource, it.stackTraceStr, state = -1)
|
||||
}
|
||||
tasks.add(search)
|
||||
}
|
||||
@@ -246,7 +246,7 @@ object Debug {
|
||||
}
|
||||
}
|
||||
.onError {
|
||||
log(debugSource, it.msg, state = -1)
|
||||
log(debugSource, it.stackTraceStr, state = -1)
|
||||
}
|
||||
tasks.add(info)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ object Debug {
|
||||
contentDebug(scope, bookSource, book, it.first(), nextChapterUrl)
|
||||
}
|
||||
.onError {
|
||||
log(debugSource, it.msg, state = -1)
|
||||
log(debugSource, it.stackTraceStr, state = -1)
|
||||
}
|
||||
tasks.add(chapterList)
|
||||
}
|
||||
@@ -284,7 +284,7 @@ object Debug {
|
||||
).onSuccess {
|
||||
log(debugSource, "︽正文页解析完成", state = 1000)
|
||||
}.onError {
|
||||
log(debugSource, it.msg, state = -1)
|
||||
log(debugSource, it.stackTraceStr, state = -1)
|
||||
}
|
||||
tasks.add(content)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import io.legado.app.model.webBook.WebBook
|
||||
import io.legado.app.service.BaseReadAloudService
|
||||
import io.legado.app.ui.book.read.page.entities.TextChapter
|
||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||
import io.legado.app.utils.msg
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
@@ -387,7 +387,7 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
}
|
||||
}.onError {
|
||||
AppLog.put("ChapterProvider ERROR", it)
|
||||
appCtx.toastOnUi("ChapterProvider ERROR:\n${it.msg}")
|
||||
appCtx.toastOnUi("ChapterProvider ERROR:\n${it.stackTraceStr}")
|
||||
}.onSuccess {
|
||||
success?.invoke()
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ class AnalyzeRule(
|
||||
log("ajax(${urlStr}) error\n${it.stackTraceToString()}")
|
||||
it.printOnDebug()
|
||||
}.getOrElse {
|
||||
it.msg
|
||||
it.stackTraceStr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.model.webBook.WebBook
|
||||
import io.legado.app.utils.msg
|
||||
import io.legado.app.utils.printOnDebug
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
|
||||
@@ -46,7 +46,7 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
|
||||
page++
|
||||
}.onError {
|
||||
it.printOnDebug()
|
||||
errorLiveData.postValue(it.msg)
|
||||
errorLiveData.postValue(it.stackTraceStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,11 +254,12 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
return@execute
|
||||
}
|
||||
}
|
||||
throw NoStackTraceException("自动换源失败")
|
||||
throw NoStackTraceException("没有搜索到 ${name}(${author})")
|
||||
}.onStart {
|
||||
ReadBook.upMsg(context.getString(R.string.source_auto_changing))
|
||||
}.onError {
|
||||
context.toastOnUi(it.msg)
|
||||
AppLog.put("自动换源失败\n${it.localizedMessage}", it)
|
||||
context.toastOnUi("自动换源失败\n${it.localizedMessage}")
|
||||
}.onFinally {
|
||||
ReadBook.upMsg(null)
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ class BgTextConfigDialog : BaseDialogFragment(R.layout.dialog_read_bg_text) {
|
||||
importConfig(it)
|
||||
}
|
||||
}.onError {
|
||||
longToast(it.msg)
|
||||
longToast(it.stackTraceStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.lib.theme.accentColor
|
||||
import io.legado.app.model.ReadBook
|
||||
import io.legado.app.ui.book.read.page.entities.TextChar
|
||||
import io.legado.app.ui.book.read.page.entities.TextColumn
|
||||
import io.legado.app.ui.book.read.page.entities.TextLine
|
||||
import io.legado.app.ui.book.read.page.entities.TextPage
|
||||
import io.legado.app.ui.book.read.page.entities.TextPos
|
||||
@@ -30,7 +30,7 @@ import io.legado.app.utils.toastOnUi
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 阅读内容界面
|
||||
* 阅读内容视图
|
||||
*/
|
||||
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
||||
var selectAble = context.getPrefBoolean(PreferKey.textSelectAble, true)
|
||||
@@ -51,7 +51,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
private var cacheIncreased = false
|
||||
private val increaseSize = 8 * 1024 * 1024
|
||||
private val maxCacheSize = 256 * 1024 * 1024
|
||||
private val reviewButtonArea: ArrayList<FloatArray> = arrayListOf()
|
||||
|
||||
//滚动参数
|
||||
private val pageFactory: TextPageFactory get() = callBack.pageFactory
|
||||
@@ -68,12 +67,18 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
callBack = activity as CallBack
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置内容
|
||||
*/
|
||||
fun setContent(textPage: TextPage) {
|
||||
this.textPage = textPage
|
||||
imagePaint.isAntiAlias = AppConfig.useAntiAlias
|
||||
invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新绘制区域
|
||||
*/
|
||||
fun upVisibleRect() {
|
||||
visibleRect.set(
|
||||
ChapterProvider.paddingLeft.toFloat(),
|
||||
@@ -102,7 +107,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
* 绘制页面
|
||||
*/
|
||||
private fun drawPage(canvas: Canvas) {
|
||||
reviewButtonArea.clear()
|
||||
var relativeOffset = relativeOffset(0)
|
||||
textPage.textLines.forEach { textLine ->
|
||||
draw(canvas, textPage, textLine, relativeOffset)
|
||||
@@ -125,6 +129,9 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制页面
|
||||
*/
|
||||
private fun draw(
|
||||
canvas: Canvas,
|
||||
textPage: TextPage,
|
||||
@@ -161,23 +168,17 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
reviewCountPaint.textSize = textPaint.textSize * 0.6F
|
||||
reviewCountPaint.color = textColor
|
||||
textLine.textChars.forEach {
|
||||
if (it.isImage) {
|
||||
drawImage(canvas, textPage, textLine, it, lineTop, lineBottom)
|
||||
} else {
|
||||
textPaint.color = textColor
|
||||
if (it.isSearchResult) {
|
||||
textPaint.color = context.accentColor
|
||||
when (it.style) {
|
||||
0 -> {
|
||||
textPaint.color = textColor
|
||||
if (it.isSearchResult) {
|
||||
textPaint.color = context.accentColor
|
||||
}
|
||||
canvas.drawText(it.charData, it.start, lineBase, textPaint)
|
||||
}
|
||||
if (it.charData == "\uD83D\uDCAC" && it.isLineEnd) {
|
||||
1 -> drawImage(canvas, textPage, textLine, it, lineTop, lineBottom)
|
||||
2 -> {
|
||||
if (textLine.reviewCount <= 0) return@forEach
|
||||
reviewButtonArea.add(
|
||||
floatArrayOf(
|
||||
it.start - textPaint.textSize * 0.4F,
|
||||
lineBase + textPaint.textSize * 0.2F,
|
||||
it.start + textPaint.textSize * 2F,
|
||||
lineBase - textPaint.textSize * 1F,
|
||||
)
|
||||
)
|
||||
canvas.drawLine(
|
||||
it.start,
|
||||
lineBase - textPaint.textSize * 2 / 5,
|
||||
@@ -243,7 +244,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
lineBase - textPaint.textSize / 6,
|
||||
reviewCountPaint
|
||||
)
|
||||
} else canvas.drawText(it.charData, it.start, lineBase, textPaint)
|
||||
}
|
||||
}
|
||||
if (it.selected) {
|
||||
canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
|
||||
@@ -259,7 +260,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
canvas: Canvas,
|
||||
textPage: TextPage,
|
||||
textLine: TextLine,
|
||||
textChar: TextChar,
|
||||
textChar: TextColumn,
|
||||
lineTop: Float,
|
||||
lineBottom: Float
|
||||
) {
|
||||
@@ -344,56 +345,62 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置滚动位置
|
||||
*/
|
||||
fun resetPageOffset() {
|
||||
pageOffset = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击评论按钮
|
||||
*/
|
||||
fun pressReviewButton(x: Float, y: Float): Boolean {
|
||||
reviewButtonArea.forEach {
|
||||
if (x in it[0]..it[2] && y in it[3]..it[1]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 长按
|
||||
*/
|
||||
fun longPress(
|
||||
x: Float,
|
||||
y: Float,
|
||||
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit,
|
||||
select: (textPos: TextPos) -> Unit,
|
||||
) {
|
||||
touch(x, y) { _, relativePos, _, lineIndex, _, charIndex, textChar ->
|
||||
if (textChar.isImage) {
|
||||
callBack.onImageLongPress(x, y, textChar.charData)
|
||||
touch(x, y) { _, textPos, _, _, textColumn ->
|
||||
if (textColumn.style == 2) return@touch
|
||||
if (textColumn.style == 1) {
|
||||
callBack.onImageLongPress(x, y, textColumn.charData)
|
||||
} else {
|
||||
if (!selectAble) return@touch
|
||||
if (textChar.charData == "\uD83D\uDCAC" && textChar.isLineEnd) return@touch
|
||||
textChar.selected = true
|
||||
textColumn.selected = true
|
||||
invalidate()
|
||||
select(relativePos, lineIndex, charIndex)
|
||||
select(textPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单击
|
||||
* @return true:已处理, false:未处理
|
||||
*/
|
||||
fun click(x: Float, y: Float): Boolean {
|
||||
var handled = false
|
||||
touch(x, y) { _, textPos, textPage, textLine, textColumn ->
|
||||
if (textColumn.style == 2) {
|
||||
context.toastOnUi("Button Pressed!")
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
return handled
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择文字
|
||||
*/
|
||||
fun selectText(
|
||||
x: Float,
|
||||
y: Float,
|
||||
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit,
|
||||
select: (textPos: TextPos) -> Unit,
|
||||
) {
|
||||
touch(x, y) { _, relativePos, _, lineIndex, _, charIndex, textChar ->
|
||||
if (textChar.charData == "\uD83D\uDCAC" && textChar.isLineEnd) return@touch
|
||||
textChar.selected = true
|
||||
touch(x, y) { _, textPos, _, _, textColumn ->
|
||||
if (textColumn.style == 2) return@touch
|
||||
textColumn.selected = true
|
||||
invalidate()
|
||||
select(relativePos, lineIndex, charIndex)
|
||||
select(textPos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,11 +408,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
* 开始选择符移动
|
||||
*/
|
||||
fun selectStartMove(x: Float, y: Float) {
|
||||
touch(x, y) { relativeOffset, relativePos, _, lineIndex, textLine, charIndex, textChar ->
|
||||
val pos = TextPos(relativePos, lineIndex, charIndex)
|
||||
if (selectStart.compare(pos) != 0) {
|
||||
if (pos.compare(selectEnd) <= 0) {
|
||||
selectStart.upData(pos = pos)
|
||||
touch(x, y) { relativeOffset, textPos, _, textLine, textChar ->
|
||||
if (selectStart.compare(textPos) != 0) {
|
||||
if (textPos.compare(selectEnd) <= 0) {
|
||||
selectStart.upData(pos = textPos)
|
||||
upSelectedStart(
|
||||
textChar.start,
|
||||
textLine.lineBottom + relativeOffset,
|
||||
@@ -421,11 +427,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
* 结束选择符移动
|
||||
*/
|
||||
fun selectEndMove(x: Float, y: Float) {
|
||||
touch(x, y) { relativeOffset, relativePos, _, lineIndex, textLine, charIndex, textChar ->
|
||||
val pos = TextPos(relativePos, lineIndex, charIndex)
|
||||
if (pos.compare(selectEnd) != 0) {
|
||||
if (pos.compare(selectStart) >= 0) {
|
||||
selectEnd.upData(pos)
|
||||
touch(x, y) { relativeOffset, textPos, _, textLine, textChar ->
|
||||
if (textPos.compare(selectEnd) != 0) {
|
||||
if (textPos.compare(selectStart) >= 0) {
|
||||
selectEnd.upData(textPos)
|
||||
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
|
||||
upSelectChars()
|
||||
}
|
||||
@@ -433,17 +438,19 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触碰位置信息
|
||||
* @param touched 回调
|
||||
*/
|
||||
private fun touch(
|
||||
x: Float,
|
||||
y: Float,
|
||||
touched: (
|
||||
relativeOffset: Float,
|
||||
relativePos: Int,
|
||||
textPos: TextPos,
|
||||
textPage: TextPage,
|
||||
lineIndex: Int,
|
||||
textLine: TextLine,
|
||||
charIndex: Int,
|
||||
textChar: TextChar
|
||||
textColumn: TextColumn
|
||||
) -> Unit
|
||||
) {
|
||||
if (!visibleRect.contains(x, y)) return
|
||||
@@ -462,9 +469,8 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
if (textChar.isTouch(x)) {
|
||||
touched.invoke(
|
||||
relativeOffset,
|
||||
relativePos, textPage,
|
||||
lineIndex, textLine,
|
||||
charIndex, textChar
|
||||
TextPos(relativePos, lineIndex, charIndex),
|
||||
textPage, textLine, textChar
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -514,7 +520,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
textPos.lineIndex = lineIndex
|
||||
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
|
||||
textPos.charIndex = charIndex
|
||||
if (textChar.charData == "\uD83D\uDCAC" && textChar.isLineEnd) continue
|
||||
if (textChar.style == 2) continue
|
||||
textChar.selected =
|
||||
textPos.compare(selectStart) >= 0 && textPos.compare(selectEnd) <= 0
|
||||
textChar.isSearchResult = textChar.selected && callBack.isSelectingSearchResult
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.legado.app.help.config.ReadTipConfig
|
||||
import io.legado.app.model.ReadBook
|
||||
import io.legado.app.ui.book.read.ReadBookActivity
|
||||
import io.legado.app.ui.book.read.page.entities.TextPage
|
||||
import io.legado.app.ui.book.read.page.entities.TextPos
|
||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||
import io.legado.app.ui.widget.BatteryView
|
||||
import io.legado.app.utils.activity
|
||||
@@ -24,7 +25,7 @@ import splitties.views.backgroundColor
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 阅读界面
|
||||
* 页面视图
|
||||
*/
|
||||
class PageView(context: Context) : FrameLayout(context) {
|
||||
|
||||
@@ -104,6 +105,9 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
isGone = ReadBookConfig.hideStatusBar || readBookActivity?.isInMultiWindow == true
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新阅读信息
|
||||
*/
|
||||
private fun upTipStyle() = binding.run {
|
||||
tvHeaderLeft.tag = null
|
||||
tvHeaderMiddle.tag = null
|
||||
@@ -189,6 +193,10 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取信息视图
|
||||
* @param tip 信息类型
|
||||
*/
|
||||
private fun getTipView(tip: Int): BatteryView? = binding.run {
|
||||
return when (tip) {
|
||||
ReadTipConfig.tipHeaderLeft -> tvHeaderLeft
|
||||
@@ -201,6 +209,9 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新背景
|
||||
*/
|
||||
fun upBg() {
|
||||
if (ReadBookConfig.bgAlpha < 100) {
|
||||
binding.vwRoot.backgroundColor = ReadBookConfig.bgMeanColor
|
||||
@@ -211,15 +222,24 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
upBgAlpha()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新背景透明度
|
||||
*/
|
||||
fun upBgAlpha() {
|
||||
binding.vwBg.alpha = ReadBookConfig.bgAlpha / 100f
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新时间信息
|
||||
*/
|
||||
fun upTime() {
|
||||
tvTime?.text = timeFormat.format(Date(System.currentTimeMillis()))
|
||||
upTimeBattery()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新电池信息
|
||||
*/
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun upBattery(battery: Int) {
|
||||
this.battery = battery
|
||||
@@ -228,6 +248,9 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
upTimeBattery()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新电池信息
|
||||
*/
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun upTimeBattery() {
|
||||
val time = timeFormat.format(Date(System.currentTimeMillis()))
|
||||
@@ -235,6 +258,9 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
tvTimeBatteryP?.text = "$time $battery%"
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置内容
|
||||
*/
|
||||
fun setContent(textPage: TextPage, resetPageOffset: Boolean = true) {
|
||||
setProgress(textPage)
|
||||
if (resetPageOffset) {
|
||||
@@ -243,14 +269,23 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
binding.contentTextView.setContent(textPage)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置无障碍文本
|
||||
*/
|
||||
fun setContentDescription(content: String) {
|
||||
binding.contentTextView.contentDescription = content
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置滚动位置
|
||||
*/
|
||||
fun resetPageOffset() {
|
||||
binding.contentTextView.resetPageOffset()
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进度
|
||||
*/
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun setProgress(textPage: TextPage) = textPage.apply {
|
||||
tvBookName?.text = ReadBook.book?.name
|
||||
@@ -260,28 +295,44 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress"
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动事件
|
||||
*/
|
||||
fun scroll(offset: Int) {
|
||||
binding.contentTextView.scroll(offset)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新是否开启选择功能
|
||||
*/
|
||||
fun upSelectAble(selectAble: Boolean) {
|
||||
binding.contentTextView.selectAble = selectAble
|
||||
}
|
||||
|
||||
fun pressReviewButton(x: Float, y: Float): Boolean {
|
||||
return binding.contentTextView.pressReviewButton(x, y - headerHeight)
|
||||
/**
|
||||
* 优先处理页面内单击
|
||||
* @return true:已处理, false:未处理
|
||||
*/
|
||||
fun onClick(x: Float, y: Float): Boolean {
|
||||
return binding.contentTextView.click(x, y - headerHeight)
|
||||
}
|
||||
|
||||
/**
|
||||
* 长按事件
|
||||
*/
|
||||
fun longPress(
|
||||
x: Float, y: Float,
|
||||
select: (relativePagePos: Int, lineIndex: Int, charIndex: Int) -> Unit,
|
||||
select: (textPos: TextPos) -> Unit,
|
||||
) {
|
||||
return binding.contentTextView.longPress(x, y - headerHeight, select)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择文本
|
||||
*/
|
||||
fun selectText(
|
||||
x: Float, y: Float,
|
||||
select: (relativePagePos: Int, lineIndex: Int, charIndex: Int) -> Unit,
|
||||
select: (textPos: TextPos) -> Unit,
|
||||
) {
|
||||
return binding.contentTextView.selectText(x, y - headerHeight, select)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@ import java.text.BreakIterator
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
/**
|
||||
* 阅读视图
|
||||
*/
|
||||
class ReadView(context: Context, attrs: AttributeSet) :
|
||||
FrameLayout(context, attrs),
|
||||
DataSource {
|
||||
@@ -223,7 +225,9 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
pressDown = false
|
||||
if (!isPageMove) {
|
||||
if (!longPressed && !pressOnTextSelected) {
|
||||
onSingleTapUp()
|
||||
if (!curPage.onClick(startX, startY)) {
|
||||
onSingleTapUp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -293,18 +297,18 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
*/
|
||||
private fun onLongPress() {
|
||||
kotlin.runCatching {
|
||||
curPage.longPress(startX, startY) { relativePos, lineIndex, charIndex ->
|
||||
curPage.longPress(startX, startY) { textPos: TextPos ->
|
||||
isTextSelected = true
|
||||
pressOnTextSelected = true
|
||||
initialTextPos.upData(relativePos, lineIndex, charIndex)
|
||||
val startPos = TextPos(relativePos, lineIndex, charIndex)
|
||||
val endPos = TextPos(relativePos, lineIndex, charIndex)
|
||||
val page = curPage.relativePage(relativePos)
|
||||
initialTextPos.upData(textPos)
|
||||
val startPos = textPos.copy()
|
||||
val endPos = textPos.copy()
|
||||
val page = curPage.relativePage(textPos.relativePagePos)
|
||||
val stringBuilder = StringBuilder()
|
||||
var cIndex = charIndex
|
||||
var lineStart = lineIndex
|
||||
var lineEnd = lineIndex
|
||||
for (index in lineIndex - 1 downTo 0) {
|
||||
var cIndex = textPos.charIndex
|
||||
var lineStart = textPos.lineIndex
|
||||
var lineEnd = textPos.lineIndex
|
||||
for (index in textPos.lineIndex - 1 downTo 0) {
|
||||
val textLine = page.getLine(index)
|
||||
if (textLine.isParagraphEnd) {
|
||||
break
|
||||
@@ -314,7 +318,7 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
cIndex += textLine.charSize
|
||||
}
|
||||
}
|
||||
for (index in lineIndex until page.lineSize) {
|
||||
for (index in textPos.lineIndex until page.lineSize) {
|
||||
val textLine = page.getLine(index)
|
||||
stringBuilder.append(textLine.text)
|
||||
lineEnd += 1
|
||||
@@ -370,9 +374,6 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
*/
|
||||
private fun onSingleTapUp() {
|
||||
when {
|
||||
curPage.pressReviewButton(startX, startY) -> {
|
||||
context.toastOnUi("Button Pressed!")
|
||||
}
|
||||
isTextSelected -> Unit
|
||||
mcRect.contains(startX, startY) -> if (!isAbortAnim) {
|
||||
click(AppConfig.clickActionMC)
|
||||
@@ -423,11 +424,15 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
* 选择文本
|
||||
*/
|
||||
private fun selectText(x: Float, y: Float) {
|
||||
curPage.selectText(x, y) { relativePagePos, lineIndex, charIndex ->
|
||||
val compare = initialTextPos.compare(relativePagePos, lineIndex, charIndex)
|
||||
curPage.selectText(x, y) { textPos ->
|
||||
val compare = initialTextPos.compare(textPos)
|
||||
when {
|
||||
compare >= 0 -> {
|
||||
curPage.selectStartMoveIndex(relativePagePos, lineIndex, charIndex)
|
||||
curPage.selectStartMoveIndex(
|
||||
textPos.relativePagePos,
|
||||
textPos.lineIndex,
|
||||
textPos.charIndex
|
||||
)
|
||||
curPage.selectEndMoveIndex(
|
||||
initialTextPos.relativePagePos,
|
||||
initialTextPos.lineIndex,
|
||||
@@ -440,7 +445,11 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
initialTextPos.lineIndex,
|
||||
initialTextPos.charIndex
|
||||
)
|
||||
curPage.selectEndMoveIndex(relativePagePos, lineIndex, charIndex)
|
||||
curPage.selectEndMoveIndex(
|
||||
textPos.relativePagePos,
|
||||
textPos.lineIndex,
|
||||
textPos.charIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,11 @@ abstract class HorizontalPageDelegate(readView: ReadView) : PageDelegate(readVie
|
||||
abortAnim()
|
||||
if (!hasNext()) return
|
||||
setDirection(PageDirection.NEXT)
|
||||
readView.setStartPoint(viewWidth.toFloat(), 1f, false)
|
||||
val y = when {
|
||||
viewHeight / 2 < startY -> viewHeight.toFloat() * 0.9f
|
||||
else -> 1f
|
||||
}
|
||||
readView.setStartPoint(viewWidth.toFloat() * 0.9f, y, false)
|
||||
onAnimStart(animationSpeed)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package io.legado.app.ui.book.read.page.entities
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 章节信息
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class TextChapter(
|
||||
val position: Int,
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package io.legado.app.ui.book.read.page.entities
|
||||
|
||||
data class TextChar(
|
||||
/**
|
||||
* 字符信息
|
||||
*/
|
||||
data class TextColumn(
|
||||
val charData: String,
|
||||
var start: Float,
|
||||
var end: Float,
|
||||
val style: Int = 0, //0:文字,1:图片,2:按钮
|
||||
var selected: Boolean = false,
|
||||
var isImage: Boolean = false,
|
||||
var isSearchResult: Boolean = false,
|
||||
var isLineEnd: Boolean = false
|
||||
var isSearchResult: Boolean = false
|
||||
) {
|
||||
|
||||
fun isTouch(x: Float): Boolean {
|
||||
@@ -4,10 +4,13 @@ import android.text.TextPaint
|
||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||
import io.legado.app.utils.textHeight
|
||||
|
||||
/**
|
||||
* 行信息
|
||||
*/
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
data class TextLine(
|
||||
var text: String = "",
|
||||
val textChars: ArrayList<TextChar> = arrayListOf(),
|
||||
val textChars: ArrayList<TextColumn> = arrayListOf(),
|
||||
val reviewCount: Int = 1,
|
||||
var lineTop: Float = 0f,
|
||||
var lineBase: Float = 0f,
|
||||
@@ -28,13 +31,13 @@ data class TextLine(
|
||||
lineBase = lineBottom - textPaint.fontMetrics.descent
|
||||
}
|
||||
|
||||
fun getTextChar(index: Int): TextChar {
|
||||
fun getTextChar(index: Int): TextColumn {
|
||||
return textChars.getOrElse(index) {
|
||||
textChars.last()
|
||||
}
|
||||
}
|
||||
|
||||
fun getTextCharReverseAt(index: Int): TextChar {
|
||||
fun getTextCharReverseAt(index: Int): TextColumn {
|
||||
return textChars[textChars.lastIndex - index]
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ import splitties.init.appCtx
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 页面信息
|
||||
*/
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
data class TextPage(
|
||||
var index: Int = 0,
|
||||
@@ -111,11 +114,11 @@ data class TextPage(
|
||||
val cw = StaticLayout.getDesiredWidth(char, ChapterProvider.contentPaint)
|
||||
val x1 = x + cw
|
||||
textLine.textChars.add(
|
||||
TextChar(
|
||||
TextColumn(
|
||||
char,
|
||||
start = x,
|
||||
end = x1,
|
||||
isLineEnd = textLine.text.length - 1 == i
|
||||
style = if (textLine.text.length - 1 == index && char == "\uD83D\uDCAC") 2 else 0
|
||||
)
|
||||
)
|
||||
x = x1
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package io.legado.app.ui.book.read.page.entities
|
||||
|
||||
/**
|
||||
* 位置信息
|
||||
*/
|
||||
data class TextPos(
|
||||
var relativePagePos: Int,
|
||||
var lineIndex: Int,
|
||||
|
||||
@@ -14,7 +14,7 @@ import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.model.ReadBook
|
||||
import io.legado.app.ui.book.read.page.entities.TextChapter
|
||||
import io.legado.app.ui.book.read.page.entities.TextChar
|
||||
import io.legado.app.ui.book.read.page.entities.TextColumn
|
||||
import io.legado.app.ui.book.read.page.entities.TextLine
|
||||
import io.legado.app.ui.book.read.page.entities.TextPage
|
||||
import io.legado.app.utils.*
|
||||
@@ -264,7 +264,7 @@ object ChapterProvider {
|
||||
Pair(0f, width.toFloat())
|
||||
}
|
||||
textLine.textChars.add(
|
||||
TextChar(charData = src, start = x + start, end = x + end, isImage = true)
|
||||
TextColumn(charData = src, start = x + start, end = x + end, style = 1)
|
||||
)
|
||||
textPages.last().textLines.add(textLine)
|
||||
}
|
||||
@@ -409,14 +409,13 @@ object ChapterProvider {
|
||||
}
|
||||
val bodyIndent = ReadBookConfig.paragraphIndent
|
||||
val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length
|
||||
for ((index, char) in bodyIndent.toStringArray().withIndex()) {
|
||||
for (char in bodyIndent.toStringArray()) {
|
||||
val x1 = x + icw
|
||||
textLine.textChars.add(
|
||||
TextChar(
|
||||
TextColumn(
|
||||
charData = char,
|
||||
start = absStartX + x,
|
||||
end = absStartX + x1,
|
||||
isLineEnd = bodyIndent.length - 1 == index
|
||||
end = absStartX + x1
|
||||
)
|
||||
)
|
||||
x = x1
|
||||
@@ -452,7 +451,7 @@ object ChapterProvider {
|
||||
words.forEachIndexed { index, char ->
|
||||
val cw = StaticLayout.getDesiredWidth(char, textPaint)
|
||||
val x1 = if (index != words.lastIndex) (x + cw + d) else (x + cw)
|
||||
addCharToLine(book, absStartX, textLine, char, x, x1, srcList)
|
||||
addCharToLine(book, absStartX, textLine, char, x, x1, index + 1 == words.size, srcList)
|
||||
x = x1
|
||||
}
|
||||
exceed(absStartX, textLine, words)
|
||||
@@ -471,10 +470,10 @@ object ChapterProvider {
|
||||
srcList: LinkedList<String>?
|
||||
) {
|
||||
var x = startX
|
||||
words.forEach { char ->
|
||||
words.forEachIndexed { index, char ->
|
||||
val cw = StaticLayout.getDesiredWidth(char, textPaint)
|
||||
val x1 = x + cw
|
||||
addCharToLine(book, absStartX, textLine, char, x, x1, srcList)
|
||||
addCharToLine(book, absStartX, textLine, char, x, x1, index + 1 == words.size, srcList)
|
||||
x = x1
|
||||
}
|
||||
exceed(absStartX, textLine, words)
|
||||
@@ -490,26 +489,27 @@ object ChapterProvider {
|
||||
char: String,
|
||||
xStart: Float,
|
||||
xEnd: Float,
|
||||
isLineEnd: Boolean,
|
||||
srcList: LinkedList<String>?
|
||||
) {
|
||||
if (srcList != null && char == srcReplaceChar) {
|
||||
val src = srcList.removeFirst()
|
||||
ImageProvider.cacheImage(book, src, ReadBook.bookSource)
|
||||
textLine.textChars.add(
|
||||
TextChar(
|
||||
TextColumn(
|
||||
charData = src,
|
||||
start = absStartX + xStart,
|
||||
end = absStartX + xEnd,
|
||||
isImage = true
|
||||
style = 1
|
||||
)
|
||||
)
|
||||
} else {
|
||||
textLine.textChars.add(
|
||||
TextChar(
|
||||
TextColumn(
|
||||
charData = char,
|
||||
start = absStartX + xStart,
|
||||
end = absStartX + xEnd,
|
||||
isLineEnd = true
|
||||
style = if (isLineEnd && char == "\uD83D\uDCAC") 2 else 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
|
||||
}.onSuccess {
|
||||
success.invoke(it)
|
||||
}.onError {
|
||||
context.toastOnUi(it.msg)
|
||||
context.toastOnUi(it.stackTraceStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import io.legado.app.data.entities.RssSource
|
||||
import io.legado.app.help.RuleComplete
|
||||
import io.legado.app.help.http.CookieStore
|
||||
import io.legado.app.utils.getClipText
|
||||
import io.legado.app.utils.msg
|
||||
import io.legado.app.utils.printOnDebug
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -74,7 +74,7 @@ class RssSourceEditViewModel(application: Application) : BaseViewModel(applicati
|
||||
finally.invoke(it)
|
||||
}
|
||||
}.onError {
|
||||
context.toastOnUi(it.msg)
|
||||
context.toastOnUi(it.stackTraceStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application)
|
||||
}.onSuccess {
|
||||
success.invoke(it)
|
||||
}.onError {
|
||||
context.toastOnUi(it.msg)
|
||||
context.toastOnUi(it.stackTraceStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -325,7 +325,7 @@ fun Context.openFileUri(uri: Uri, type: String? = null) {
|
||||
try {
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
toastOnUi(e.msg)
|
||||
toastOnUi(e.stackTraceStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.legado.app.utils
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
val Throwable.msg: String
|
||||
val Throwable.stackTraceStr: String
|
||||
get() {
|
||||
val stackTrace = stackTraceToString()
|
||||
val lMsg = this.localizedMessage ?: "noErrorMsg"
|
||||
|
||||
@@ -26,4 +26,4 @@ android.experimental.enableNewResourceShrinker.preciseShrinking=true
|
||||
# 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=105.0.5195.77
|
||||
CronetVersion=105.0.5195.79
|
||||
|
||||
Reference in New Issue
Block a user