优化
This commit is contained in:
@@ -177,6 +177,20 @@ object ReadBook : CoroutineScope by MainScope() {
|
|||||||
return hasNextPage
|
return hasNextPage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun moveToPrevPage(): Boolean {
|
||||||
|
var hasPrevPage = false
|
||||||
|
curTextChapter?.let {
|
||||||
|
val prevPagePos = it.getPrevPageLength(durChapterPos)
|
||||||
|
if (prevPagePos >= 0) {
|
||||||
|
hasPrevPage = true
|
||||||
|
durChapterPos = prevPagePos
|
||||||
|
callBack?.upContent()
|
||||||
|
saveRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasPrevPage
|
||||||
|
}
|
||||||
|
|
||||||
fun moveToNextChapter(upContent: Boolean): Boolean {
|
fun moveToNextChapter(upContent: Boolean): Boolean {
|
||||||
if (durChapterIndex < chapterSize - 1) {
|
if (durChapterIndex < chapterSize - 1) {
|
||||||
durChapterPos = 0
|
durChapterPos = 0
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ abstract class BaseReadAloudService : BaseService(),
|
|||||||
BitmapFactory.decodeResource(appCtx.resources, R.drawable.icon_read_book)
|
BitmapFactory.decodeResource(appCtx.resources, R.drawable.icon_read_book)
|
||||||
var pageChanged = false
|
var pageChanged = false
|
||||||
private var ttsProgress = 0
|
private var ttsProgress = 0
|
||||||
|
private var toLast = false
|
||||||
|
var paragraphStartPos = 0
|
||||||
|
private var readAloudByPage = false
|
||||||
|
|
||||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
@@ -164,19 +167,40 @@ abstract class BaseReadAloudService : BaseService(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun newReadAloud(play: Boolean, pageIndex: Int, startPos: Int) {
|
private fun newReadAloud(play: Boolean, pageIndex: Int, startPos: Int) {
|
||||||
lifecycleScope.launch(IO) {
|
execute(executeContext = IO) {
|
||||||
this@BaseReadAloudService.pageIndex = pageIndex
|
this@BaseReadAloudService.pageIndex = pageIndex
|
||||||
textChapter = ReadBook.curTextChapter
|
textChapter = ReadBook.curTextChapter
|
||||||
val textChapter = textChapter ?: return@launch
|
val textChapter = textChapter ?: return@execute
|
||||||
nowSpeak = 0
|
|
||||||
readAloudNumber = textChapter.getReadLength(pageIndex) + startPos
|
readAloudNumber = textChapter.getReadLength(pageIndex) + startPos
|
||||||
val readAloudByPage = getPrefBoolean(PreferKey.readAloudByPage)
|
readAloudByPage = getPrefBoolean(PreferKey.readAloudByPage)
|
||||||
contentList = textChapter.getNeedReadAloud(pageIndex, readAloudByPage, startPos)
|
contentList = textChapter.getNeedReadAloud(0, readAloudByPage, 0)
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.filter { it.isNotEmpty() }
|
.filter { it.isNotEmpty() }
|
||||||
|
var pos = startPos
|
||||||
|
val page = textChapter.getPage(pageIndex)!!
|
||||||
|
if (pos > 0) {
|
||||||
|
for (paragraph in page.paragraphs) {
|
||||||
|
val tmp = pos - paragraph.length - 1
|
||||||
|
if (tmp < 0) break
|
||||||
|
pos = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nowSpeak = textChapter.getParagraphNum(readAloudNumber + 1, readAloudByPage) - 1
|
||||||
|
if (!readAloudByPage && pos == 0) {
|
||||||
|
pos = page.lines.first().chapterPosition -
|
||||||
|
textChapter.paragraphs[nowSpeak].chapterPosition
|
||||||
|
}
|
||||||
|
paragraphStartPos = pos
|
||||||
|
if (toLast) {
|
||||||
|
toLast = false
|
||||||
|
readAloudNumber = textChapter.getLastParagraphPosition()
|
||||||
|
nowSpeak = contentList.lastIndex
|
||||||
|
}
|
||||||
launch(Main) {
|
launch(Main) {
|
||||||
if (play) play() else pageChanged = true
|
if (play) play() else pageChanged = true
|
||||||
}
|
}
|
||||||
|
}.onError {
|
||||||
|
AppLog.put("启动朗读出错\n${it.localizedMessage}", it, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,9 +249,24 @@ abstract class BaseReadAloudService : BaseService(),
|
|||||||
if (nowSpeak > 0) {
|
if (nowSpeak > 0) {
|
||||||
playStop()
|
playStop()
|
||||||
nowSpeak--
|
nowSpeak--
|
||||||
readAloudNumber -= contentList[nowSpeak].length.minus(1)
|
readAloudNumber -= contentList[nowSpeak].length + 1 + paragraphStartPos
|
||||||
|
paragraphStartPos = 0
|
||||||
|
textChapter?.let {
|
||||||
|
val paragraphs = if (readAloudByPage) {
|
||||||
|
it.pageParagraphs
|
||||||
|
} else {
|
||||||
|
it.paragraphs
|
||||||
|
}
|
||||||
|
if (!paragraphs[nowSpeak].isParagraphEnd) readAloudNumber++
|
||||||
|
if (readAloudNumber < it.getReadLength(pageIndex)) {
|
||||||
|
pageIndex--
|
||||||
|
ReadBook.moveToPrevPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upTtsProgress(readAloudNumber + 1)
|
||||||
play()
|
play()
|
||||||
} else {
|
} else {
|
||||||
|
toLast = true
|
||||||
ReadBook.moveToPrevChapter(true)
|
ReadBook.moveToPrevChapter(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,8 +274,10 @@ abstract class BaseReadAloudService : BaseService(),
|
|||||||
private fun nextP() {
|
private fun nextP() {
|
||||||
if (nowSpeak < contentList.size - 1) {
|
if (nowSpeak < contentList.size - 1) {
|
||||||
playStop()
|
playStop()
|
||||||
readAloudNumber += contentList[nowSpeak].length.plus(1)
|
readAloudNumber += contentList[nowSpeak].length.plus(1) - paragraphStartPos
|
||||||
|
paragraphStartPos = 0
|
||||||
nowSpeak++
|
nowSpeak++
|
||||||
|
upTtsProgress(readAloudNumber + 1)
|
||||||
play()
|
play()
|
||||||
} else {
|
} else {
|
||||||
nextChapter()
|
nextChapter()
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import com.script.ScriptException
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.AppLog
|
import io.legado.app.constant.AppLog
|
||||||
import io.legado.app.constant.AppPattern
|
import io.legado.app.constant.AppPattern
|
||||||
import io.legado.app.constant.EventBus
|
|
||||||
import io.legado.app.data.entities.HttpTTS
|
import io.legado.app.data.entities.HttpTTS
|
||||||
import io.legado.app.exception.ConcurrentException
|
import io.legado.app.exception.ConcurrentException
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
@@ -22,7 +21,6 @@ import io.legado.app.model.ReadBook
|
|||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
import io.legado.app.utils.MD5Utils
|
import io.legado.app.utils.MD5Utils
|
||||||
import io.legado.app.utils.postEvent
|
|
||||||
import io.legado.app.utils.printOnDebug
|
import io.legado.app.utils.printOnDebug
|
||||||
import io.legado.app.utils.servicePendingIntent
|
import io.legado.app.utils.servicePendingIntent
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
@@ -90,10 +88,12 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
|
|
||||||
override fun playStop() {
|
override fun playStop() {
|
||||||
exoPlayer.stop()
|
exoPlayer.stop()
|
||||||
|
playIndexJob?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun playNext() {
|
private fun playNext() {
|
||||||
readAloudNumber += contentList[nowSpeak].length + 1
|
readAloudNumber += contentList[nowSpeak].length + 1 - paragraphStartPos
|
||||||
|
paragraphStartPos = 0
|
||||||
if (nowSpeak < contentList.lastIndex) {
|
if (nowSpeak < contentList.lastIndex) {
|
||||||
nowSpeak++
|
nowSpeak++
|
||||||
} else {
|
} else {
|
||||||
@@ -111,10 +111,14 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
contentList.forEachIndexed { index, content ->
|
contentList.forEachIndexed { index, content ->
|
||||||
ensureActive()
|
ensureActive()
|
||||||
if (index < nowSpeak) return@forEachIndexed
|
if (index < nowSpeak) return@forEachIndexed
|
||||||
val fileName = md5SpeakFileName(content)
|
var text = content
|
||||||
val speakText = content.replace(AppPattern.notReadAloudRegex, "")
|
if (paragraphStartPos > 0 && index == nowSpeak) {
|
||||||
|
text = text.substring(paragraphStartPos)
|
||||||
|
}
|
||||||
|
val fileName = md5SpeakFileName(text)
|
||||||
|
val speakText = text.replace(AppPattern.notReadAloudRegex, "")
|
||||||
if (speakText.isEmpty()) {
|
if (speakText.isEmpty()) {
|
||||||
AppLog.put("阅读段落内容为空,使用无声音频代替。\n朗读文本:$content")
|
AppLog.put("阅读段落内容为空,使用无声音频代替。\n朗读文本:$text")
|
||||||
createSilentSound(fileName)
|
createSilentSound(fileName)
|
||||||
} else if (!hasSpeakFile(fileName)) {
|
} else if (!hasSpeakFile(fileName)) {
|
||||||
runCatching {
|
runCatching {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import io.legado.app.R
|
|||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.constant.AppLog
|
import io.legado.app.constant.AppLog
|
||||||
import io.legado.app.constant.AppPattern
|
import io.legado.app.constant.AppPattern
|
||||||
import io.legado.app.constant.EventBus
|
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.MediaHelp
|
import io.legado.app.help.MediaHelp
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
@@ -100,7 +99,10 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
|
|||||||
val contentList = contentList
|
val contentList = contentList
|
||||||
for (i in nowSpeak until contentList.size) {
|
for (i in nowSpeak until contentList.size) {
|
||||||
ensureActive()
|
ensureActive()
|
||||||
val text = contentList[i]
|
var text = contentList[i]
|
||||||
|
if (paragraphStartPos > 0 && i == nowSpeak) {
|
||||||
|
text = text.substring(paragraphStartPos)
|
||||||
|
}
|
||||||
if (text.matches(AppPattern.notReadAloudRegex)) {
|
if (text.matches(AppPattern.notReadAloudRegex)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -178,7 +180,8 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
|
|||||||
override fun onDone(s: String) {
|
override fun onDone(s: String) {
|
||||||
//跳过全标点段落
|
//跳过全标点段落
|
||||||
do {
|
do {
|
||||||
readAloudNumber += contentList[nowSpeak].length + 1
|
readAloudNumber += contentList[nowSpeak].length + 1 - paragraphStartPos
|
||||||
|
paragraphStartPos = 0
|
||||||
nowSpeak++
|
nowSpeak++
|
||||||
if (nowSpeak >= contentList.size) {
|
if (nowSpeak >= contentList.size) {
|
||||||
nextChapter()
|
nextChapter()
|
||||||
|
|||||||
@@ -247,4 +247,16 @@ class RemoteBookActivity : BaseImportBookActivity<RemoteBookViewModel>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
override fun addToBookShelfAgain(remoteBook: RemoteBook) {
|
||||||
|
alert(getString(R.string.sure), "是否重新加入书架?") {
|
||||||
|
yesButton {
|
||||||
|
binding.refreshProgressBar.isAutoLoading = true
|
||||||
|
viewModel.addToBookshelf(hashSetOf(remoteBook)) {
|
||||||
|
binding.refreshProgressBar.isAutoLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,6 +90,14 @@ class RemoteBookAdapter(context: Context, val callBack: CallBack) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
holder.itemView.setOnLongClickListener {
|
||||||
|
getItem(holder.layoutPosition)?.let { remoteBook ->
|
||||||
|
if (remoteBook.isOnBookShelf) {
|
||||||
|
callBack.addToBookShelfAgain(remoteBook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun upCheckableCount() {
|
private fun upCheckableCount() {
|
||||||
@@ -144,5 +152,6 @@ class RemoteBookAdapter(context: Context, val callBack: CallBack) :
|
|||||||
fun openDir(remoteBook: RemoteBook)
|
fun openDir(remoteBook: RemoteBook)
|
||||||
fun upCountView()
|
fun upCountView()
|
||||||
fun startRead(remoteBook: RemoteBook)
|
fun startRead(remoteBook: RemoteBook)
|
||||||
|
fun addToBookShelfAgain(remoteBook: RemoteBook)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.legado.app.ui.book.manage
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import io.legado.app.base.BaseViewModel
|
import io.legado.app.base.BaseViewModel
|
||||||
|
import io.legado.app.constant.AppLog
|
||||||
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.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
@@ -52,22 +53,21 @@ class BookshelfManageViewModel(application: Application) : BaseViewModel(applica
|
|||||||
batchChangeSourceCoroutine?.cancel()
|
batchChangeSourceCoroutine?.cancel()
|
||||||
batchChangeSourceCoroutine = execute {
|
batchChangeSourceCoroutine = execute {
|
||||||
books.forEachIndexed { index, book ->
|
books.forEachIndexed { index, book ->
|
||||||
batchChangeSourceProcessLiveData.postValue("${index + 1}/${books.size}")
|
batchChangeSourceProcessLiveData.postValue("${index + 1} / ${books.size}")
|
||||||
if (book.isLocal) return@forEachIndexed
|
if (book.isLocal) return@forEachIndexed
|
||||||
if (book.origin == source.bookSourceUrl) return@forEachIndexed
|
if (book.origin == source.bookSourceUrl) return@forEachIndexed
|
||||||
WebBook.preciseSearchAwait(this, source, book.name, book.author)
|
val newBook = WebBook.preciseSearchAwait(this, source, book.name, book.author)
|
||||||
.onFailure {
|
.onFailure {
|
||||||
context.toastOnUi("获取书籍出错\n${it.localizedMessage}")
|
AppLog.put("获取书籍出错\n${it.localizedMessage}", it, true)
|
||||||
}.getOrNull()?.let { newBook ->
|
}.getOrNull() ?: return@forEachIndexed
|
||||||
WebBook.getChapterListAwait(source, newBook)
|
WebBook.getChapterListAwait(source, newBook)
|
||||||
.onFailure {
|
.onFailure {
|
||||||
context.toastOnUi("获取目录出错\n${it.localizedMessage}")
|
AppLog.put("获取目录出错\n${it.localizedMessage}", it, true)
|
||||||
}.getOrNull()?.let { toc ->
|
}.getOrNull()?.let { toc ->
|
||||||
book.migrateTo(newBook, toc)
|
book.migrateTo(newBook, toc)
|
||||||
book.removeType(BookType.updateError)
|
book.removeType(BookType.updateError)
|
||||||
appDb.bookDao.insert(newBook)
|
appDb.bookDao.insert(newBook)
|
||||||
appDb.bookChapterDao.insert(*toc.toTypedArray())
|
appDb.bookChapterDao.insert(*toc.toTypedArray())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onStart {
|
}.onStart {
|
||||||
|
|||||||
@@ -40,6 +40,38 @@ data class TextChapter(
|
|||||||
|
|
||||||
val pageSize: Int get() = pages.size
|
val pageSize: Int get() = pages.size
|
||||||
|
|
||||||
|
val paragraphs by lazy {
|
||||||
|
paragraphsInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
val pageParagraphs by lazy {
|
||||||
|
pageParagraphsInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
val paragraphsInternal: ArrayList<TextParagraph>
|
||||||
|
get() {
|
||||||
|
val paragraphs = arrayListOf<TextParagraph>()
|
||||||
|
pages.forEach {
|
||||||
|
it.lines.forEach loop@{ line ->
|
||||||
|
if (line.paragraphNum <= 0) return@loop
|
||||||
|
if (paragraphs.lastIndex < line.paragraphNum - 1) {
|
||||||
|
paragraphs.add(TextParagraph(line.paragraphNum))
|
||||||
|
}
|
||||||
|
paragraphs[line.paragraphNum - 1].textLines.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paragraphs
|
||||||
|
}
|
||||||
|
|
||||||
|
val pageParagraphsInternal: List<TextParagraph>
|
||||||
|
get() = pages.map {
|
||||||
|
it.paragraphs
|
||||||
|
}.flatten().also {
|
||||||
|
it.forEachIndexed { index, textParagraph ->
|
||||||
|
textParagraph.num = index + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param index 页数
|
* @param index 页数
|
||||||
* @return 是否是最后一页
|
* @return 是否是最后一页
|
||||||
@@ -73,6 +105,18 @@ data class TextChapter(
|
|||||||
return getReadLength(pageIndex + 1)
|
return getReadLength(pageIndex + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param length 当前页面文字在章节中的位置
|
||||||
|
* @return 上一页位置,如果没有上一页返回-1
|
||||||
|
*/
|
||||||
|
fun getPrevPageLength(length: Int): Int {
|
||||||
|
val pageIndex = getPageIndexByCharIndex(length)
|
||||||
|
if (pageIndex - 1 < 0) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return getReadLength(pageIndex - 1)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取内容
|
* 获取内容
|
||||||
*/
|
*/
|
||||||
@@ -116,6 +160,27 @@ data class TextChapter(
|
|||||||
return stringBuilder.substring(startPos).toString()
|
return stringBuilder.substring(startPos).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getParagraphNum(
|
||||||
|
position: Int,
|
||||||
|
pageSplit: Boolean,
|
||||||
|
): Int {
|
||||||
|
val paragraphs = if (pageSplit) {
|
||||||
|
pageParagraphs
|
||||||
|
} else {
|
||||||
|
paragraphs
|
||||||
|
}
|
||||||
|
paragraphs.forEach { paragraph ->
|
||||||
|
if (position in paragraph.chapterIndices) {
|
||||||
|
return paragraph.num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLastParagraphPosition(): Int {
|
||||||
|
return pageParagraphs.last().chapterPosition
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 根据索引位置获取所在页
|
* @return 根据索引位置获取所在页
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ data class TextLine(
|
|||||||
var lineBase: Float = 0f,
|
var lineBase: Float = 0f,
|
||||||
var lineBottom: Float = 0f,
|
var lineBottom: Float = 0f,
|
||||||
var indentWidth: Float = 0f,
|
var indentWidth: Float = 0f,
|
||||||
|
var paragraphNum: Int = 0,
|
||||||
|
var chapterPosition: Int = 0,
|
||||||
|
var pagePosition: Int = 0,
|
||||||
val isTitle: Boolean = false,
|
val isTitle: Boolean = false,
|
||||||
var isParagraphEnd: Boolean = false,
|
var isParagraphEnd: Boolean = false,
|
||||||
var isReadAloud: Boolean = false,
|
var isReadAloud: Boolean = false,
|
||||||
@@ -28,6 +31,7 @@ data class TextLine(
|
|||||||
val charSize: Int get() = textColumns.size
|
val charSize: Int get() = textColumns.size
|
||||||
val lineStart: Float get() = textColumns.firstOrNull()?.start ?: 0f
|
val lineStart: Float get() = textColumns.firstOrNull()?.start ?: 0f
|
||||||
val lineEnd: Float get() = textColumns.lastOrNull()?.end ?: 0f
|
val lineEnd: Float get() = textColumns.lastOrNull()?.end ?: 0f
|
||||||
|
val chapterIndices: IntRange get() = chapterPosition..chapterPosition + charSize
|
||||||
|
|
||||||
fun addColumn(column: BaseColumn) {
|
fun addColumn(column: BaseColumn) {
|
||||||
textColumns.add(column)
|
textColumns.add(column)
|
||||||
|
|||||||
@@ -36,6 +36,23 @@ data class TextPage(
|
|||||||
val searchResult = hashSetOf<TextColumn>()
|
val searchResult = hashSetOf<TextColumn>()
|
||||||
var isMsgPage: Boolean = false
|
var isMsgPage: Boolean = false
|
||||||
|
|
||||||
|
val paragraphs by lazy {
|
||||||
|
paragraphsInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
val paragraphsInternal: ArrayList<TextParagraph> get() {
|
||||||
|
val paragraphs = arrayListOf<TextParagraph>()
|
||||||
|
val lines = textLines.filter { it.paragraphNum > 0 }
|
||||||
|
val offset = lines.first().paragraphNum - 1
|
||||||
|
lines.forEach { line ->
|
||||||
|
if (paragraphs.lastIndex < line.paragraphNum - offset - 1) {
|
||||||
|
paragraphs.add(TextParagraph(0))
|
||||||
|
}
|
||||||
|
paragraphs[line.paragraphNum - offset - 1].textLines.add(line)
|
||||||
|
}
|
||||||
|
return paragraphs
|
||||||
|
}
|
||||||
|
|
||||||
fun addLine(line: TextLine) {
|
fun addLine(line: TextLine) {
|
||||||
textLines.add(line)
|
textLines.add(line)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package io.legado.app.ui.book.read.page.entities
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
data class TextParagraph(
|
||||||
|
var num: Int,
|
||||||
|
val textLines: ArrayList<TextLine> = arrayListOf(),
|
||||||
|
) : Iterable<TextLine> {
|
||||||
|
val text: String get() = textLines.joinToString("") { it.text }
|
||||||
|
val length: Int get() = text.length
|
||||||
|
val chapterIndices: IntRange get() = first().chapterPosition..last().chapterPosition + last().charSize
|
||||||
|
val chapterPosition: Int get() = first().chapterPosition
|
||||||
|
val realNum: Int get() = first().paragraphNum
|
||||||
|
val isParagraphEnd: Boolean get() = last().isParagraphEnd
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<TextLine> {
|
||||||
|
return textLines.iterator()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -417,10 +417,23 @@ object ChapterProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val sbLength = stringBuilder.length
|
||||||
stringBuilder.append(words)
|
stringBuilder.append(words)
|
||||||
if (textLine.isParagraphEnd) {
|
if (textLine.isParagraphEnd) {
|
||||||
stringBuilder.append("\n")
|
stringBuilder.append("\n")
|
||||||
}
|
}
|
||||||
|
val lastLine = textPages.last().lines.lastOrNull { it.paragraphNum > 0 }
|
||||||
|
?: textPages.getOrNull(textPages.lastIndex - 1)?.lines?.lastOrNull { it.paragraphNum > 0 }
|
||||||
|
val paragraphNum = when {
|
||||||
|
lastLine == null -> 1
|
||||||
|
lastLine.isParagraphEnd -> lastLine.paragraphNum + 1
|
||||||
|
else -> lastLine.paragraphNum
|
||||||
|
}
|
||||||
|
textLine.paragraphNum = paragraphNum
|
||||||
|
textLine.chapterPosition = textPages.foldIndexed(sbLength) { index, acc, textPage ->
|
||||||
|
acc + if (index == textPages.lastIndex) 0 else textPage.text.length
|
||||||
|
}
|
||||||
|
textLine.pagePosition = sbLength
|
||||||
textPages.last().addLine(textLine)
|
textPages.last().addLine(textLine)
|
||||||
textLine.upTopBottom(durY, textPaint)
|
textLine.upTopBottom(durY, textPaint)
|
||||||
durY += textPaint.textHeight * lineSpacingExtra
|
durY += textPaint.textHeight * lineSpacingExtra
|
||||||
|
|||||||
Reference in New Issue
Block a user