From f6bd4a1704bdbde0462d0dc4bb5bb62fb8c1c8ee Mon Sep 17 00:00:00 2001 From: Horis <8674809+821938089@users.noreply.github.com> Date: Sat, 22 Feb 2025 15:37:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/help/glide/ImageLoader.kt | 5 +- .../legado/app/service/ExportBookService.kt | 380 ++++++------------ .../read/page/provider/TextChapterLayout.kt | 2 +- .../io/legado/app/ui/main/rss/RssAdapter.kt | 2 +- .../app/ui/widget/image/CoverImageView.kt | 2 +- .../io/legado/app/utils/FileDocExtensions.kt | 13 + .../app/utils/RequestManagerExtensions.kt | 9 + 7 files changed, 139 insertions(+), 274 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/glide/ImageLoader.kt b/app/src/main/java/io/legado/app/help/glide/ImageLoader.kt index 145a58b69..335cbc5de 100644 --- a/app/src/main/java/io/legado/app/help/glide/ImageLoader.kt +++ b/app/src/main/java/io/legado/app/help/glide/ImageLoader.kt @@ -12,7 +12,6 @@ import io.legado.app.utils.isAbsUrl import io.legado.app.utils.isContentScheme import io.legado.app.utils.isDataUrl import io.legado.app.utils.lifecycle -import splitties.init.appCtx import java.io.File //https://bumptech.github.io/glide/doc/generatedapi.html @@ -37,8 +36,8 @@ object ImageLoader { } } - fun load(lifecycle: Lifecycle, path: String?): RequestBuilder { - val requestManager = Glide.with(appCtx).lifecycle(lifecycle) + fun load(context: Context, lifecycle: Lifecycle, path: String?): RequestBuilder { + val requestManager = Glide.with(context).lifecycle(lifecycle) return when { path.isNullOrEmpty() -> requestManager.load(path) path.isDataUrl() -> requestManager.load(path) diff --git a/app/src/main/java/io/legado/app/service/ExportBookService.kt b/app/src/main/java/io/legado/app/service/ExportBookService.kt index 5bbf6787f..7ff38237f 100644 --- a/app/src/main/java/io/legado/app/service/ExportBookService.kt +++ b/app/src/main/java/io/legado/app/service/ExportBookService.kt @@ -2,12 +2,7 @@ package io.legado.app.service import android.annotation.SuppressLint import android.content.Intent -import android.graphics.Bitmap -import android.net.Uri -import android.os.Build -import android.util.ArraySet import androidx.core.app.NotificationCompat -import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.lifecycleScope import com.bumptech.glide.Glide import io.legado.app.R @@ -31,7 +26,6 @@ import io.legado.app.help.book.isLocalModified import io.legado.app.help.config.AppConfig import io.legado.app.model.localBook.LocalBook import io.legado.app.ui.book.cache.CacheActivity -import io.legado.app.utils.DocumentUtils import io.legado.app.utils.FileDoc import io.legado.app.utils.FileUtils import io.legado.app.utils.HtmlFormatter @@ -39,21 +33,19 @@ import io.legado.app.utils.MD5Utils import io.legado.app.utils.NetworkUtils import io.legado.app.utils.activityPendingIntent import io.legado.app.utils.cnCompare -import io.legado.app.utils.createFolderIfNotExist +import io.legado.app.utils.createFileIfNotExist +import io.legado.app.utils.delete import io.legado.app.utils.find -import io.legado.app.utils.isContentScheme import io.legado.app.utils.list import io.legado.app.utils.mapAsync import io.legado.app.utils.mapAsyncIndexed -import io.legado.app.utils.outputStream +import io.legado.app.utils.openOutputStream import io.legado.app.utils.postEvent import io.legado.app.utils.servicePendingIntent import io.legado.app.utils.toastOnUi -import io.legado.app.utils.writeBytes -import kotlinx.coroutines.Deferred +import io.legado.app.utils.writeFile import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Job -import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.collectIndexed @@ -65,6 +57,7 @@ import me.ag2s.epublib.domain.Date import me.ag2s.epublib.domain.EpubBook import me.ag2s.epublib.domain.FileResourceProvider import me.ag2s.epublib.domain.LazyResource +import me.ag2s.epublib.domain.LazyResourceProvider import me.ag2s.epublib.domain.Metadata import me.ag2s.epublib.domain.Resource import me.ag2s.epublib.domain.TOCReference @@ -73,8 +66,6 @@ import me.ag2s.epublib.epub.EpubWriterProcessor import me.ag2s.epublib.util.ResourceUtil import splitties.init.appCtx import splitties.systemservices.notificationManager -import java.io.ByteArrayOutputStream -import java.io.File import java.nio.charset.Charset import java.util.concurrent.ConcurrentHashMap import kotlin.coroutines.coroutineContext @@ -197,15 +188,15 @@ class ExportBookService : BaseService() { upExportNotification() if (exportConfig.type == "epub") { if (exportConfig.epubScope.isNullOrBlank()) { - exportEPUB(exportConfig.path, book) + exportEpub(exportConfig.path, book) } else { CustomExporter( - paresScope(exportConfig.epubScope), + exportConfig.epubScope, exportConfig.epubSize ).export(exportConfig.path, book) } } else { - export(exportConfig.path, book) + exportTxt(exportConfig.path, book) } exportMsg[book.bookUrl] = getString(R.string.export_success) } catch (e: Throwable) { @@ -239,40 +230,33 @@ class ExportBookService : BaseService() { val src: String ) - private suspend fun export(path: String, book: Book) { + private suspend fun exportTxt(path: String, book: Book) { exportMsg.remove(book.bookUrl) postEvent(EventBus.EXPORT_BOOK, book.bookUrl) - if (path.isContentScheme()) { - val uri = Uri.parse(path) - val doc = DocumentFile.fromTreeUri(this@ExportBookService, uri) - ?: throw NoStackTraceException("获取导出文档失败") - export(doc, book) - } else { - export(File(path).createFolderIfNotExist(), book) - } + val fileDoc = FileDoc.fromDir(path) + exportTxt(fileDoc, book) } - private suspend fun export(doc: DocumentFile, book: Book) { + private suspend fun exportTxt(fileDoc: FileDoc, book: Book) { val filename = book.getExportFileName("txt") - DocumentUtils.delete(doc, filename) - val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename) - ?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹") + fileDoc.find(filename)?.delete() + + val bookDoc = fileDoc.createFileIfNotExist(filename) val charset = Charset.forName(AppConfig.exportCharset) - contentResolver.openOutputStream(bookDoc.uri, "wa")?.bufferedWriter(charset)?.use { bw -> + bookDoc.openOutputStream().getOrThrow().bufferedWriter(charset).use { bw -> getAllContents(book) { text, srcList -> bw.write(text) srcList?.forEach { val vFile = BookHelp.getImage(book, it.src) if (vFile.exists()) { - DocumentUtils.createFileIfNotExist( - doc, + fileDoc.createFileIfNotExist( "${it.index}-${MD5Utils.md5Encode16(it.src)}.jpg", subDirs = arrayOf( "${book.name}_${book.author}", "images", it.chapterTitle ) - )?.writeBytes(this, vFile.readBytes()) + ).writeFile(vFile) } } } @@ -283,34 +267,6 @@ class ExportBookService : BaseService() { } } - private suspend fun export(file: File, book: Book) { - val filename = book.getExportFileName("txt") - val bookPath = FileUtils.getPath(file, filename) - val bookFile = FileUtils.createFileWithReplace(bookPath) - val charset = Charset.forName(AppConfig.exportCharset) - val bw = bookFile.outputStream(true).bufferedWriter(charset) - bw.use { - getAllContents(book) { text, srcList -> - it.write(text) - srcList?.forEach { - val vFile = BookHelp.getImage(book, it.src) - if (vFile.exists()) { - FileUtils.createFileIfNotExist( - file, - "${book.name}_${book.author}", - "images", - it.chapterTitle, - "${it.index}-${MD5Utils.md5Encode16(it.src)}.jpg" - ).writeBytes(vFile.readBytes()) - } - } - } - } - if (AppConfig.exportToWebDav) { - AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename) // 导出到webdav - } - } - private suspend fun getAllContents( book: Book, append: (text: String, srcList: ArrayList?) -> Unit @@ -381,62 +337,20 @@ class ExportBookService : BaseService() { } } - /** - * 解析范围字符串 - * - * @param scope 范围字符串 - * @return 范围 - * - * @since 2023/5/22 - * @author Discut - */ - @SuppressLint("ObsoleteSdkInt") - private fun paresScope(scope: String): Set { - val split = scope.split(",") - - @Suppress("RemoveExplicitTypeArguments") - val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ArraySet() - } else { - HashSet() - } - for (s in split) { - val v = s.split("-") - if (v.size != 2) { - result.add(s.toInt() - 1) - continue - } - val left = v[0].toInt() - val right = v[1].toInt() - if (left > right) { - AppLog.put("Error expression : $s; left > right") - continue - } - for (i in left..right) - result.add(i - 1) - } - return result - } - /** * 导出Epub */ - private suspend fun exportEPUB(path: String, book: Book) { + private suspend fun exportEpub(path: String, book: Book) { exportMsg.remove(book.bookUrl) postEvent(EventBus.EXPORT_BOOK, book.bookUrl) - if (path.isContentScheme()) { - val uri = Uri.parse(path) - val doc = DocumentFile.fromTreeUri(this@ExportBookService, uri) - ?: throw NoStackTraceException("获取导出文档失败") - exportEpub(doc, book) - } else { - exportEpub(File(path).createFolderIfNotExist(), book) - } + val fileDoc = FileDoc.fromDir(path) + exportEpub(fileDoc, book) } - private suspend fun exportEpub(doc: DocumentFile, book: Book) { + private suspend fun exportEpub(fileDoc: FileDoc, book: Book) { val filename = book.getExportFileName("epub") - DocumentUtils.delete(doc, filename) + fileDoc.find(filename)?.delete() + val epubBook = EpubBook() epubBook.version = "2.0" //set metadata @@ -444,54 +358,22 @@ class ExportBookService : BaseService() { //set cover setCover(book, epubBook) //set css - val contentModel = setAssets(doc, book, epubBook) + val contentModel = setAssets(fileDoc, book, epubBook) //设置正文 setEpubContent(contentModel, book, epubBook) - DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc -> - contentResolver.openOutputStream(bookDoc.uri, "wa")?.buffered().use { bookOs -> - EpubWriter().write(epubBook, bookOs) - } - if (AppConfig.exportToWebDav) { - // 导出到webdav - AppWebDav.exportWebDav(bookDoc.uri, filename) - } + + val bookDoc = fileDoc.createFileIfNotExist(filename) + bookDoc.openOutputStream().getOrThrow().buffered().use { bookOs -> + EpubWriter().write(epubBook, bookOs) } - } - - private suspend fun exportEpub(file: File, book: Book) { - val filename = book.getExportFileName("epub") - val epubBook = EpubBook() - epubBook.version = "2.0" - //set metadata - setEpubMetadata(book, epubBook) - //set cover - setCover(book, epubBook) - //set css - val contentModel = setAssets(file, book, epubBook) - - val bookPath = FileUtils.getPath(file, filename) - val bookFile = FileUtils.createFileWithReplace(bookPath) - //设置正文 - setEpubContent(contentModel, book, epubBook) - bookFile.outputStream().buffered().use { - EpubWriter().write(epubBook, it) - } if (AppConfig.exportToWebDav) { // 导出到webdav - AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename) + AppWebDav.exportWebDav(bookDoc.uri, filename) } } - private fun setAssets(doc: DocumentFile, book: Book, epubBook: EpubBook): String { - return setAssets(FileDoc.fromDocumentFile(doc), book, epubBook) - } - - private fun setAssets(file: File, book: Book, epubBook: EpubBook): String { - return setAssets(FileDoc.fromFile(file), book, epubBook) - } - private fun setAssets(doc: FileDoc, book: Book, epubBook: EpubBook): String { val customPath = doc.find("Asset") val contentModel = if (customPath == null) {//使用内置模板 @@ -618,16 +500,15 @@ class ExportBookService : BaseService() { private fun setCover(book: Book, epubBook: EpubBook) { kotlin.runCatching { - val bitmap = Glide.with(this) - .asBitmap() + val file = Glide.with(this) + .asFile() .load(book.getDisplayCover()) .submit() .get() - val byteArray = ByteArrayOutputStream().use { - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it) - it.toByteArray() + val provider = LazyResourceProvider { _ -> + file.inputStream() } - epubBook.coverImage = Resource(byteArray, "Images/cover.jpg") + epubBook.coverImage = LazyResource(provider, "Images/cover.jpg") }.onFailure { AppLog.put("获取书籍封面出错\n${it.localizedMessage}", it) } @@ -758,7 +639,9 @@ class ExportBookService : BaseService() { * @param scope 导出范围 * @param size epub 文件包含最大章节数 */ - inner class CustomExporter(private var scope: Set, private val size: Int) { + inner class CustomExporter(scopeStr: String, private val size: Int) { + + private var scope = parseScope(scopeStr) /** * 导出Epub @@ -775,78 +658,35 @@ class ExportBookService : BaseService() { val currentTimeMillis = System.currentTimeMillis() val count = appDb.bookChapterDao.getChapterCount(book.bookUrl) scope = scope.filter { it < count }.toHashSet() - when (path.isContentScheme()) { - true -> { - val uri = Uri.parse(path) - val doc = DocumentFile.fromTreeUri(this@ExportBookService, uri) - ?: throw NoStackTraceException("获取导出文档失败") - val (contentModel, epubList) = createEpubs(book, doc) - val asyncBlocks = ArrayList>(epubList.size) - var progressBar = 0.0 - epubList.forEachIndexed { index, ep -> - val (filename, epubBook) = ep - coroutineScope { - val asyncBlock = async { - //设置正文 - setEpubContent( - contentModel, - book, - epubBook, - index - ) { _, _ -> - // 将章节写入内存时更新进度条 - postEvent(EventBus.EXPORT_BOOK, book.bookUrl) - progressBar += book.totalChapterNum.toDouble() / scope.size / 2 - exportProgress[book.bookUrl] = progressBar.toInt() - } - save2Drive(filename, epubBook, doc) { total, _ -> - //写入硬盘时更新进度条 - progressBar += book.totalChapterNum.toDouble() / epubList.size / total / 2 - postEvent(EventBus.EXPORT_BOOK, book.bookUrl) - exportProgress[book.bookUrl] = progressBar.toInt() - } - } - asyncBlocks.add(asyncBlock) - } - } - asyncBlocks.forEach { it.await() } - } - false -> { - val file = File(path).createFolderIfNotExist() - val (contentModel, epubList) = createEpubs(book, null) - val asyncBlocks = ArrayList>(epubList.size) - var progressBar = 0.0 - epubList.forEachIndexed { index, ep -> - val (filename, epubBook) = ep - coroutineScope { - val asyncBlock = async { - //设置正文 - setEpubContent( - contentModel, - book, - epubBook, - index - ) { _, _ -> - postEvent(EventBus.EXPORT_BOOK, book.bookUrl) - exportProgress[book.bookUrl] = - exportProgress[book.bookUrl]?.plus(book.totalChapterNum / scope.size) - ?: 1 - } - save2Drive(filename, epubBook, file) { total, _ -> - //设置进度 - progressBar += book.totalChapterNum.toDouble() / epubList.size / total / 2 - postEvent(EventBus.EXPORT_BOOK, book.bookUrl) - exportProgress[book.bookUrl] = progressBar.toInt() - } - } - asyncBlocks.add(asyncBlock) - } - } - asyncBlocks.forEach { it.await() } + val fileDoc = FileDoc.fromDir(path) + + val (contentModel, epubList) = createEpubs(book, fileDoc) + var progressBar = 0.0 + epubList.forEachIndexed { index, ep -> + val (filename, epubBook) = ep + //设置正文 + setEpubContent( + contentModel, + book, + epubBook, + index + ) { _, _ -> + // 将章节写入内存时更新进度条 + postEvent(EventBus.EXPORT_BOOK, book.bookUrl) + progressBar += book.totalChapterNum.toDouble() / scope.size / 2 + exportProgress[book.bookUrl] = progressBar.toInt() + } + save2Drive(filename, epubBook, fileDoc) { total, _ -> + //写入硬盘时更新进度条 + progressBar += book.totalChapterNum.toDouble() / epubList.size / total / 2 + postEvent(EventBus.EXPORT_BOOK, book.bookUrl) + exportProgress[book.bookUrl] = progressBar.toInt() } } - AppLog.put("分割导出书籍 ${book.name} 一共耗时 ${System.currentTimeMillis() - currentTimeMillis}") + + val elapsed = System.currentTimeMillis() - currentTimeMillis + AppLog.put("分割导出书籍 ${book.name} 一共耗时 $elapsed") } @@ -878,7 +718,7 @@ class ExportBookService : BaseService() { } } // val totalChapterNum = book.totalChapterNum / scope.size - if (chapterList.size == 0) { + if (chapterList.isEmpty()) { throw RuntimeException("书籍<${book.name}>(${epubBookIndex + 1})未找到章节信息") } chapterList = chapterList.subList( @@ -930,23 +770,22 @@ class ExportBookService : BaseService() { * 创建多个epub 对象 * * 分割epub时,一个书籍需要创建多个epub对象 - * @param doc 导出文档 * @param book 书籍 + * @param fileDoc 导出文件夹文档 * * @return <内容模板字符串, > */ private fun createEpubs( book: Book, - doc: DocumentFile?, + fileDoc: FileDoc ): Pair>> { val paresNumOfEpub = paresNumOfEpub(scope.size, size) val result: MutableList> = ArrayList(paresNumOfEpub) var contentModel = "" for (i in 1..paresNumOfEpub) { val filename = book.getExportFileName("epub", i) - doc?.let { - DocumentUtils.delete(it, filename) - } + fileDoc.find(filename)?.delete() + val epubBook = EpubBook() epubBook.version = "2.0" //set metadata @@ -954,9 +793,7 @@ class ExportBookService : BaseService() { //set cover setCover(book, epubBook) //set css - contentModel = doc?.let { - setAssets(it, book, epubBook) - } ?: setAssets(book, epubBook) + contentModel = setAssets(fileDoc, book, epubBook) // add epubBook result.add(Pair(filename, epubBook)) @@ -970,47 +807,23 @@ class ExportBookService : BaseService() { private suspend fun save2Drive( filename: String, epubBook: EpubBook, - doc: DocumentFile, + fileDoc: FileDoc, callback: (total: Int, progress: Int) -> Unit ) { - DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc -> - contentResolver.openOutputStream(bookDoc.uri, "wa")?.buffered().use { bookOs -> - EpubWriter() - .setCallback(object : EpubWriterProcessor.Callback { - override fun onProgressing(total: Int, progress: Int) { - callback(total, progress) - } - }) - .write(epubBook, bookOs) - } - if (AppConfig.exportToWebDav) { - // 导出到webdav - AppWebDav.exportWebDav(bookDoc.uri, filename) - } + val bookDoc = fileDoc.createFileIfNotExist(filename) + bookDoc.openOutputStream().getOrThrow().buffered().use { bookOs -> + EpubWriter() + .setCallback(object : EpubWriterProcessor.Callback { + override fun onProgressing(total: Int, progress: Int) { + callback(total, progress) + } + }) + .write(epubBook, bookOs) } - } - /** - * 保存文件到 设备 - */ - private suspend fun save2Drive( - filename: String, - epubBook: EpubBook, - file: File, - callback: (total: Int, progress: Int) -> Unit - ) { - val bookPath = FileUtils.getPath(file, filename) - val bookFile = FileUtils.createFileWithReplace(bookPath) - EpubWriter() - .setCallback(object : EpubWriterProcessor.Callback { - override fun onProgressing(total: Int, progress: Int) { - callback(total, progress) - } - }) - .write(epubBook, bookFile.outputStream().buffered()) if (AppConfig.exportToWebDav) { // 导出到webdav - AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename) + AppWebDav.exportWebDav(bookDoc.uri, filename) } } @@ -1028,5 +841,36 @@ class ExportBookService : BaseService() { } return result } + + /** + * 解析范围字符串 + * + * @param scope 范围字符串 + * @return 范围 + * + * @since 2023/5/22 + * @author Discut + */ + private fun parseScope(scope: String): Set { + val split = scope.split(",") + + val result = linkedSetOf() + for (s in split) { + val v = s.split("-") + if (v.size != 2) { + result.add(s.toInt() - 1) + continue + } + val left = v[0].toInt() + val right = v[1].toInt() + if (left > right) { + AppLog.put("Error expression : $s; left > right") + continue + } + for (i in left..right) + result.add(i - 1) + } + return result + } } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt index b2ed402d5..05075210a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt @@ -89,7 +89,7 @@ class TextChapterLayout( var exception: Throwable? = null - var channel = Channel(Int.MAX_VALUE) + var channel = Channel(Channel.UNLIMITED) init { diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt index 256683122..36f2bca83 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt @@ -32,7 +32,7 @@ class RssAdapter(context: Context, val callBack: CallBack, val lifecycle: Lifecy tvName.text = item.sourceName val options = RequestOptions() .set(OkHttpModelLoader.sourceOriginOption, item.sourceUrl) - ImageLoader.load(lifecycle, item.sourceIcon) + ImageLoader.load(context, lifecycle, item.sourceIcon) .apply(options) .centerCrop() .placeholder(R.drawable.image_rss) diff --git a/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt b/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt index 0d3041224..c9c503231 100644 --- a/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt +++ b/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt @@ -198,7 +198,7 @@ class CoverImageView @JvmOverloads constructor( options = options.set(OkHttpModelLoader.sourceOriginOption, sourceOrigin) } val builder = if (lifecycle != null) { - ImageLoader.load(lifecycle, path) + ImageLoader.load(context, lifecycle, path) } else { ImageLoader.load(context, path)//Glide自动识别http://,content://和file:// } diff --git a/app/src/main/java/io/legado/app/utils/FileDocExtensions.kt b/app/src/main/java/io/legado/app/utils/FileDocExtensions.kt index eaca1850c..5cf97ac70 100644 --- a/app/src/main/java/io/legado/app/utils/FileDocExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/FileDocExtensions.kt @@ -8,6 +8,7 @@ import android.database.Cursor import android.net.Uri import android.os.ParcelFileDescriptor import android.provider.DocumentsContract +import androidx.core.net.toUri import androidx.documentfile.provider.DocumentFile import io.legado.app.exception.NoStackTraceException import splitties.init.appCtx @@ -67,6 +68,10 @@ data class FileDoc( companion object { + fun fromDir(path: String): FileDoc { + return fromUri(path.toUri(), true) + } + fun fromUri(uri: Uri, isDir: Boolean): FileDoc { if (uri.isContentScheme()) { val doc = if (isDir) { @@ -284,6 +289,14 @@ fun FileDoc.writeText(text: String) { } } +fun FileDoc.writeFile(file: File) { + openOutputStream().getOrThrow().use { out -> + file.inputStream().use { + it.copyTo(out) + } + } +} + fun FileDoc.delete() { asFile()?.let { FileUtils.delete(it, true) diff --git a/app/src/main/java/io/legado/app/utils/RequestManagerExtensions.kt b/app/src/main/java/io/legado/app/utils/RequestManagerExtensions.kt index 9184ea5d2..647fe0524 100644 --- a/app/src/main/java/io/legado/app/utils/RequestManagerExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/RequestManagerExtensions.kt @@ -3,9 +3,18 @@ package io.legado.app.utils import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner +import com.bumptech.glide.Glide import com.bumptech.glide.RequestManager +import splitties.init.appCtx + +private val applicationRequestManager by lazy { + Glide.with(appCtx) +} fun RequestManager.lifecycle(lifecycle: Lifecycle): RequestManager { + if (this === applicationRequestManager) { + return this + } val observer = object : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) = onStart()