mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
本地书籍采用MediaStore保存到Download/books
This commit is contained in:
parent
23447a51f2
commit
4a2e62d413
90
app/src/main/java/io/legado/app/help/BookMediaStore.kt
Normal file
90
app/src/main/java/io/legado/app/help/BookMediaStore.kt
Normal file
@ -0,0 +1,90 @@
|
||||
package io.legado.app.help
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import io.legado.app.constant.AppConst
|
||||
import io.legado.app.utils.FileDoc
|
||||
import io.legado.app.utils.FileUtils.getMimeType
|
||||
import splitties.init.appCtx
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
object BookMediaStore {
|
||||
private val DOWNLOAD_DIR =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
|
||||
fun insertBook(doc: DocumentFile): Uri? {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val bookDetails = ContentValues().apply {
|
||||
put(MediaStore.Downloads.RELATIVE_PATH, "Download${File.separator}books")
|
||||
put(MediaStore.MediaColumns.DISPLAY_NAME, doc.name)
|
||||
put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(doc.name!!))
|
||||
put(MediaStore.MediaColumns.SIZE, doc.length())
|
||||
}
|
||||
appCtx.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, bookDetails)
|
||||
} else {
|
||||
val destinyFile = File(DOWNLOAD_DIR, doc.name!!)
|
||||
FileProvider.getUriForFile(appCtx, AppConst.authority, destinyFile)
|
||||
}?.also { uri ->
|
||||
appCtx.contentResolver.openOutputStream(uri).use { outputStream ->
|
||||
val brr = ByteArray(1024)
|
||||
var len: Int
|
||||
val bufferedInputStream = appCtx.contentResolver.openInputStream(doc.uri)!!
|
||||
while ((bufferedInputStream.read(brr, 0, brr.size).also { len = it }) != -1) {
|
||||
outputStream?.write(brr, 0, len)
|
||||
}
|
||||
outputStream?.flush()
|
||||
bufferedInputStream.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBook(name: String): FileDoc? {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val projection = arrayOf(
|
||||
MediaStore.Downloads._ID,
|
||||
MediaStore.Downloads.DISPLAY_NAME,
|
||||
MediaStore.Downloads.SIZE,
|
||||
MediaStore.Downloads.DATE_MODIFIED
|
||||
)
|
||||
val selection =
|
||||
"${MediaStore.Downloads.RELATIVE_PATH} like 'Download${File.separator}books${File.separator}%'"
|
||||
val sortOrder = "${MediaStore.Downloads.DISPLAY_NAME} ASC"
|
||||
appCtx.contentResolver.query(
|
||||
MediaStore.Downloads.EXTERNAL_CONTENT_URI,
|
||||
projection,
|
||||
selection,
|
||||
emptyArray(),
|
||||
sortOrder
|
||||
)?.use {
|
||||
val idColumn = it.getColumnIndex(projection[0])
|
||||
val nameColumn = it.getColumnIndex(projection[1])
|
||||
val sizeColumn = it.getColumnIndex(projection[2])
|
||||
val dateColumn = it.getColumnIndex(projection[3])
|
||||
if (it.moveToNext()) {
|
||||
val id = it.getLong(idColumn)
|
||||
return FileDoc(
|
||||
name = it.getString(nameColumn),
|
||||
isDir = false,
|
||||
size = it.getLong(sizeColumn),
|
||||
date = Date(it.getLong(dateColumn)),
|
||||
uri = ContentUris.withAppendedId(
|
||||
MediaStore.Downloads.EXTERNAL_CONTENT_URI,
|
||||
id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
package io.legado.app.ui.association
|
||||
|
||||
import android.app.Application
|
||||
import android.app.RecoverableSecurityException
|
||||
import android.content.IntentSender
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.legado.app.help.BookMediaStore
|
||||
import io.legado.app.model.NoStackTraceException
|
||||
import io.legado.app.model.localBook.LocalBook
|
||||
import io.legado.app.utils.isContentScheme
|
||||
import io.legado.app.utils.isJson
|
||||
import io.legado.app.utils.readText
|
||||
import splitties.init.appCtx
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
@ -18,7 +24,9 @@ class FileAssociationViewModel(application: Application) : BaseAssociationViewMo
|
||||
val importReplaceRuleLive = MutableLiveData<String>()
|
||||
val openBookLiveData = MutableLiveData<String>()
|
||||
val errorLiveData = MutableLiveData<String>()
|
||||
val recoverErrorLiveData = MutableLiveData<IntentSender>()
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
fun dispatchIndent(uri: Uri, finally: (title: String, msg: String) -> Unit) {
|
||||
execute {
|
||||
//如果是普通的url,需要根据返回的内容判断是什么
|
||||
@ -27,34 +35,68 @@ class FileAssociationViewModel(application: Application) : BaseAssociationViewMo
|
||||
File(uri.path.toString()).readText()
|
||||
} else {
|
||||
DocumentFile.fromSingleUri(context, uri)?.readText(context)
|
||||
}
|
||||
content?.let {
|
||||
if (it.isJson()) {
|
||||
//暂时根据文件内容判断属于什么
|
||||
when {
|
||||
content.contains("bookSourceUrl") ->
|
||||
importBookSourceLive.postValue(it)
|
||||
content.contains("sourceUrl") ->
|
||||
importRssSourceLive.postValue(it)
|
||||
content.contains("pattern") ->
|
||||
importReplaceRuleLive.postValue(it)
|
||||
content.contains("themeName") ->
|
||||
importTheme(content, finally)
|
||||
content.contains("name") && content.contains("rule") ->
|
||||
importTextTocRule(content, finally)
|
||||
content.contains("name") && content.contains("url") ->
|
||||
importHttpTTS(content, finally)
|
||||
else -> errorLiveData.postValue("格式不对")
|
||||
} ?: throw NoStackTraceException("文件不存在")
|
||||
if (content.isJson()) {
|
||||
//暂时根据文件内容判断属于什么
|
||||
when {
|
||||
content.contains("bookSourceUrl") ->
|
||||
importBookSourceLive.postValue(content)
|
||||
content.contains("sourceUrl") ->
|
||||
importRssSourceLive.postValue(content)
|
||||
content.contains("pattern") ->
|
||||
importReplaceRuleLive.postValue(content)
|
||||
content.contains("themeName") ->
|
||||
importTheme(content, finally)
|
||||
content.contains("name") && content.contains("rule") ->
|
||||
importTextTocRule(content, finally)
|
||||
content.contains("name") && content.contains("url") ->
|
||||
importHttpTTS(content, finally)
|
||||
else -> errorLiveData.postValue("格式不对")
|
||||
}
|
||||
} else {
|
||||
if (uri.isContentScheme()) {
|
||||
val doc = DocumentFile.fromSingleUri(appCtx, uri)!!
|
||||
val bookDoc = BookMediaStore.getBook(doc.name!!)
|
||||
if (bookDoc == null) {
|
||||
val bookUri = BookMediaStore.insertBook(doc)
|
||||
val book = LocalBook.importFile(bookUri!!)
|
||||
openBookLiveData.postValue(book.bookUrl)
|
||||
} else {
|
||||
if (doc.lastModified() > bookDoc.date.time) {
|
||||
context.contentResolver.openOutputStream(bookDoc.uri)
|
||||
.use { outputStream ->
|
||||
val brr = ByteArray(1024)
|
||||
var len: Int
|
||||
val bufferedInputStream =
|
||||
appCtx.contentResolver.openInputStream(doc.uri)!!
|
||||
while ((bufferedInputStream.read(brr, 0, brr.size)
|
||||
.also { len = it }) != -1
|
||||
) {
|
||||
outputStream?.write(brr, 0, len)
|
||||
}
|
||||
outputStream?.flush()
|
||||
bufferedInputStream.close()
|
||||
}
|
||||
}
|
||||
val book = LocalBook.importFile(bookDoc.uri)
|
||||
openBookLiveData.postValue(book.bookUrl)
|
||||
}
|
||||
} else {
|
||||
val book = LocalBook.importFile(uri)
|
||||
openBookLiveData.postValue(book.bookUrl)
|
||||
}
|
||||
} ?: throw NoStackTraceException("文件不存在")
|
||||
}
|
||||
} else {
|
||||
onLineImportLive.postValue(uri)
|
||||
}
|
||||
}.onError {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
if (it is RecoverableSecurityException) {
|
||||
val intentSender = it.userAction.actionIntent.intentSender
|
||||
recoverErrorLiveData.postValue(intentSender)
|
||||
return@onError
|
||||
}
|
||||
}
|
||||
Timber.e(it)
|
||||
errorLiveData.postValue(it.localizedMessage)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user