本地书籍无权限则保存到自己选定的文件夹

This commit is contained in:
gedoor 2021-12-30 23:24:09 +08:00
parent 5434759d20
commit c51b60c8e6

View File

@ -1,6 +1,8 @@
package io.legado.app.model.localBook package io.legado.app.model.localBook
import android.net.Uri import android.net.Uri
import android.system.Os
import android.system.OsConstants.SEEK_SET
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
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
@ -12,9 +14,9 @@ import io.legado.app.utils.StringUtils
import io.legado.app.utils.isContentScheme import io.legado.app.utils.isContentScheme
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
import java.io.FileDescriptor
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.InputStream
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.regex.Matcher import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
@ -26,26 +28,30 @@ class TextFile(private val book: Book) {
@Throws(FileNotFoundException::class) @Throws(FileNotFoundException::class)
fun getChapterList(): ArrayList<BookChapter> { fun getChapterList(): ArrayList<BookChapter> {
return getBookInputStream(book).use { return getBookFD(book).let { fd ->
val buffer = ByteArray(BUFFER_SIZE) try {
it.read(buffer) val buffer = ByteArray(BUFFER_SIZE)
if (book.charset == null) { Os.read(fd, buffer, 0, BUFFER_SIZE)
book.charset = EncodingDetect.getEncode(buffer) if (book.charset == null) {
book.charset = EncodingDetect.getEncode(buffer)
}
charset = book.fileCharset()
val rulePattern = if (book.tocUrl.isNotEmpty()) {
Pattern.compile(book.tocUrl, Pattern.MULTILINE)
} else {
tocRules.addAll(getTocRules())
null
}
analyze(fd, book, rulePattern)
} finally {
Os.close(fd)
} }
charset = book.fileCharset()
val rulePattern = if (book.tocUrl.isNotEmpty()) {
Pattern.compile(book.tocUrl, Pattern.MULTILINE)
} else {
tocRules.addAll(getTocRules())
null
}
analyze(it, book, rulePattern)
} }
} }
@Throws(FileNotFoundException::class) @Throws(FileNotFoundException::class)
private fun analyze( private fun analyze(
bookIs: InputStream, bookFd: FileDescriptor,
book: Book, book: Book,
pattern: Pattern? pattern: Pattern?
): ArrayList<BookChapter> { ): ArrayList<BookChapter> {
@ -53,8 +59,8 @@ class TextFile(private val book: Book) {
var tocRule: TxtTocRule? = null var tocRule: TxtTocRule? = null
val buffer = ByteArray(BUFFER_SIZE) val buffer = ByteArray(BUFFER_SIZE)
val rulePattern = pattern ?: let { val rulePattern = pattern ?: let {
bookIs.skip(0) Os.lseek(bookFd, 0, SEEK_SET)
val length = bookIs.read(buffer) val length = Os.read(bookFd, buffer, 0, BUFFER_SIZE)
val content = String(buffer, 0, length, charset) val content = String(buffer, 0, length, charset)
tocRule = getTocRule(content) tocRule = getTocRule(content)
tocRule?.let { tocRule?.let {
@ -63,7 +69,7 @@ class TextFile(private val book: Book) {
} }
//加载章节 //加载章节
//获取到的块起始点,在文件中的位置 //获取到的块起始点,在文件中的位置
bookIs.skip(0) Os.lseek(bookFd, 0, SEEK_SET)
var curOffset: Long = 0 var curOffset: Long = 0
//block的个数 //block的个数
var blockPos = 0 var blockPos = 0
@ -72,7 +78,7 @@ class TextFile(private val book: Book) {
var allLength = 0 var allLength = 0
//获取文件中的数据到buffer直到没有数据为止 //获取文件中的数据到buffer直到没有数据为止
while (bookIs.read(buffer).also { length = it } > 0) { while (Os.read(bookFd, buffer, 0, BUFFER_SIZE).also { length = it } > 0) {
blockPos++ blockPos++
//如果存在Chapter //如果存在Chapter
if (rulePattern != null) { if (rulePattern != null) {
@ -83,7 +89,7 @@ class TextFile(private val book: Book) {
blockContent = blockContent.substring(0, lastN) blockContent = blockContent.substring(0, lastN)
length = blockContent.toByteArray(charset).size length = blockContent.toByteArray(charset).size
allLength += length allLength += length
bookIs.skip(allLength.toLong()) Os.lseek(bookFd, allLength.toLong(), SEEK_SET)
} }
//当前Block下使过的String的指针 //当前Block下使过的String的指针
var seekPos = 0 var seekPos = 0
@ -99,7 +105,7 @@ class TextFile(private val book: Book) {
if (curOffset + chapterLength - lastStart > 50000 && pattern == null) { if (curOffset + chapterLength - lastStart > 50000 && pattern == null) {
//移除不匹配的规则 //移除不匹配的规则
tocRules.remove(tocRule) tocRules.remove(tocRule)
return analyze(bookIs, book, null) return analyze(bookFd, book, null)
} }
//如果 seekPos == 0 && nextChapterPos != 0 表示当前block处前面有一段内容 //如果 seekPos == 0 && nextChapterPos != 0 表示当前block处前面有一段内容
//第一种情况一定是序章 第二种情况是上一个章节的内容 //第一种情况一定是序章 第二种情况是上一个章节的内容
@ -155,7 +161,7 @@ class TextFile(private val book: Book) {
if (seekPos == 0 && length > 50000 && pattern == null) { if (seekPos == 0 && length > 50000 && pattern == null) {
//移除不匹配的规则 //移除不匹配的规则
tocRules.remove(tocRule) tocRules.remove(tocRule)
return analyze(bookIs, book, null) return analyze(bookFd, book, null)
} }
} else { //进行本地虚拟分章 } else { //进行本地虚拟分章
//章节在buffer的偏移量 //章节在buffer的偏移量
@ -261,9 +267,13 @@ class TextFile(private val book: Book) {
fun getContent(book: Book, bookChapter: BookChapter): String { fun getContent(book: Book, bookChapter: BookChapter): String {
val count = (bookChapter.end!! - bookChapter.start!!).toInt() val count = (bookChapter.end!! - bookChapter.start!!).toInt()
val content = ByteArray(count) val content = ByteArray(count)
getBookInputStream(book).use { getBookFD(book).let { fd ->
it.skip(bookChapter.start!!) try {
it.read(content) Os.lseek(fd, bookChapter.start!!, SEEK_SET)
Os.read(fd, content, 0, count)
} finally {
Os.close(fd)
}
} }
return String(content, book.fileCharset()) return String(content, book.fileCharset())
.substringAfter(bookChapter.title) .substringAfter(bookChapter.title)
@ -271,12 +281,12 @@ class TextFile(private val book: Book) {
} }
@Throws(FileNotFoundException::class) @Throws(FileNotFoundException::class)
private fun getBookInputStream(book: Book): InputStream { private fun getBookFD(book: Book): FileDescriptor {
if (book.bookUrl.isContentScheme()) { if (book.bookUrl.isContentScheme()) {
val uri = Uri.parse(book.bookUrl) val uri = Uri.parse(book.bookUrl)
return appCtx.contentResolver.openInputStream(uri)!! return appCtx.contentResolver.openFileDescriptor(uri, "r")!!.fileDescriptor
} }
return FileInputStream(File(book.bookUrl)) return FileInputStream(File(book.bookUrl)).fd
} }
private fun getTocRules(): List<TxtTocRule> { private fun getTocRules(): List<TxtTocRule> {