Merge remote-tracking branch 'origin/master'

This commit is contained in:
kunfei
2023-03-17 08:40:36 +08:00
7 changed files with 76 additions and 24 deletions
@@ -37,6 +37,10 @@ object BookType {
*/ */
const val local = 0b100000000 const val local = 0b100000000
/**
* 512 压缩包 表明书籍文件是从压缩包内解压来的
*/
const val archive = 0b1000000000
@Target(AnnotationTarget.VALUE_PARAMETER) @Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
@@ -355,7 +355,7 @@ object DatabaseMigrations {
db.execSQL( db.execSQL(
""" """
update books set type = type | ${BookType.local} update books set type = type | ${BookType.local}
where origin = '${BookType.localTag}' or origin like '${BookType.webDavTag}%' where origin like '${BookType.localTag}%' or origin like '${BookType.webDavTag}%'
""".trimIndent() """.trimIndent()
) )
} }
@@ -55,6 +55,19 @@ val Book.isWebFile: Boolean
val Book.isUpError: Boolean val Book.isUpError: Boolean
get() = isType(BookType.updateError) get() = isType(BookType.updateError)
val Book.isArchive: Boolean
get() = isType(BookType.archive)
val Book.archiveName: String?
get() {
return if (isArchive) {
// local_book::archive.rar
origin.substring(BookType.localTag.length + 2)
} else {
null
}
}
fun Book.contains(word: String?): Boolean { fun Book.contains(word: String?): Boolean {
if (word.isNullOrEmpty()) { if (word.isNullOrEmpty()) {
return true return true
@@ -139,7 +152,7 @@ fun Book.removeLocalUriCache() {
fun Book.getRemoteUrl(): String? { fun Book.getRemoteUrl(): String? {
if (origin.startsWith(BookType.webDavTag)) { if (origin.startsWith(BookType.webDavTag)) {
return origin.substring(8) return origin.substring(BookType.webDavTag.length)
} }
return null return null
} }
@@ -175,7 +188,7 @@ fun Book.upType() {
BookSourceType.file -> BookType.webFile BookSourceType.file -> BookType.webFile
else -> BookType.text else -> BookType.text
} }
if (origin == "loc_book" || origin.startsWith(BookType.webDavTag)) { if (origin == BookType.localTag || origin.startsWith(BookType.webDavTag)) {
type = type or BookType.local type = type or BookType.local
} }
} }
@@ -190,18 +190,24 @@ object LocalBook {
/* 导入压缩包内的书籍 */ /* 导入压缩包内的书籍 */
fun importArchiveFile( fun importArchiveFile(
uri: Uri, archiveFileUri: Uri,
saveFileName: String? = null, saveFileName: String? = null,
filter: ((String) -> Boolean)? = null filter: ((String) -> Boolean)? = null
): List<Book> { ): List<Book> {
val files = ArchiveUtils.deCompress(uri, filter = filter) val archiveFileDoc = FileDoc.fromUri(archiveFileUri, false)
val files = ArchiveUtils.deCompress(archiveFileDoc, filter = filter)
if (files.isEmpty()) throw NoStackTraceException(appCtx.getString(R.string.unsupport_archivefile_entry)) if (files.isEmpty()) throw NoStackTraceException(appCtx.getString(R.string.unsupport_archivefile_entry))
return files.map { return files.map {
saveBookFile( saveBookFile(
FileInputStream(it), FileInputStream(it),
saveFileName ?: it.name saveFileName ?: it.name
).let { ).let {
importFile(it) importFile(it).apply {
//附加压缩包文件名 以便判断书籍导入界面压缩包是否导入
origin = "${BookType.localTag}::${archiveFileDoc.name}"
addType(BookType.archive)
save()
}
} }
} }
} }
@@ -13,9 +13,7 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.primaryTextColor import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.document.HandleFileContract import io.legado.app.ui.document.HandleFileContract
import io.legado.app.utils.applyTint import io.legado.app.utils.*
import io.legado.app.utils.hideSoftInput
import io.legado.app.utils.startActivity
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlin.coroutines.resume import kotlin.coroutines.resume
@@ -11,6 +11,7 @@ import androidx.documentfile.provider.DocumentFile
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.DialogEditTextBinding
@@ -132,7 +133,7 @@ class ImportBookActivity : BaseImportBookActivity<ImportBookViewModel>(),
initRootDoc() initRootDoc()
} }
launch { launch {
appDb.bookDao.flowLocalUri().conflate().collect { appDb.bookDao.flowLocal().conflate().collect {
adapter.upBookHas(it) adapter.upBookHas(it)
} }
} }
@@ -300,6 +301,21 @@ class ImportBookActivity : BaseImportBookActivity<ImportBookViewModel>(),
binding.selectActionBar.upCountView(adapter.selectedUris.size, adapter.checkableCount) binding.selectActionBar.upCountView(adapter.selectedUris.size, adapter.checkableCount)
} }
override fun startRead(bookUrl: String) = startReadBook(bookUrl) override fun startRead(fileDoc: FileDoc) {
if (!ArchiveUtils.isArchive(fileDoc.name)) {
startReadBook(fileDoc.toString())
} else {
val fileNames = ArchiveUtils.getArchiveFilesName(fileDoc) {
it.matches(AppPattern.bookFileRegex)
}
if (fileNames.size == 1) {
appDb.bookDao.getBookByFileName(fileNames[0])?.let {
startReadBook(it.bookUrl)
}
} else {
//onArchiveFileClick(fileNames)
}
}
}
} }
@@ -8,7 +8,10 @@ import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.data.entities.Book
import io.legado.app.databinding.ItemImportBookBinding import io.legado.app.databinding.ItemImportBookBinding
import io.legado.app.help.book.archiveName
import io.legado.app.help.book.isArchive
import io.legado.app.utils.* import io.legado.app.utils.*
@@ -16,7 +19,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
RecyclerAdapter<FileDoc, ItemImportBookBinding>(context) { RecyclerAdapter<FileDoc, ItemImportBookBinding>(context) {
val selectedUris = hashSetOf<String>() val selectedUris = hashSetOf<String>()
var checkableCount = 0 var checkableCount = 0
private val bookNamesOnBookShelf = arrayListOf<String>() private val bookOrArchiveNamesOnBookShelf = arrayListOf<String>()
override fun getViewBinding(parent: ViewGroup): ItemImportBookBinding { override fun getViewBinding(parent: ViewGroup): ItemImportBookBinding {
return ItemImportBookBinding.inflate(inflater, parent, false) return ItemImportBookBinding.inflate(inflater, parent, false)
@@ -41,7 +44,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
llBrief.gone() llBrief.gone()
cbSelect.isChecked = false cbSelect.isChecked = false
} else { } else {
if (bookNamesOnBookShelf.contains(item.name)) { if (isOnBookShelf(item)) {
ivIcon.setImageResource(R.drawable.ic_book_has) ivIcon.setImageResource(R.drawable.ic_book_has)
ivIcon.visible() ivIcon.visible()
cbSelect.invisible() cbSelect.invisible()
@@ -67,7 +70,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
if (it.isDir) { if (it.isDir) {
callBack.nextDoc(it) callBack.nextDoc(it)
} else if (!bookNamesOnBookShelf.contains(it.name)) { } else if (!isOnBookShelf(it)) {
if (!selectedUris.contains(it.toString())) { if (!selectedUris.contains(it.toString())) {
selectedUris.add(it.toString()) selectedUris.add(it.toString())
} else { } else {
@@ -77,18 +80,30 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
callBack.upCountView() callBack.upCountView()
} else { } else {
/* 点击开始阅读 */ /* 点击开始阅读 */
callBack.startRead(it.toString()) callBack.startRead(it)
} }
} }
} }
} }
private fun isOnBookShelf(fileDoc: FileDoc): Boolean {
return bookOrArchiveNamesOnBookShelf.contains(fileDoc.name)
}
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun upBookHas(bookUrls: List<String>) { fun upBookHas(books: List<Book>) {
bookNamesOnBookShelf.clear() bookOrArchiveNamesOnBookShelf.clear()
bookUrls.forEach { books.forEach {
val path = Uri.decode(it) if (it.isArchive) {
bookNamesOnBookShelf.add(FileUtils.getName(path)) bookOrArchiveNamesOnBookShelf.add(
it.archiveName!!
)
} else {
val path = Uri.decode(it.bookUrl)
bookOrArchiveNamesOnBookShelf.add(
FileUtils.getName(path)
)
}
} }
notifyDataSetChanged() notifyDataSetChanged()
upCheckableCount() upCheckableCount()
@@ -97,7 +112,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
private fun upCheckableCount() { private fun upCheckableCount() {
checkableCount = 0 checkableCount = 0
getItems().forEach { getItems().forEach {
if (!it.isDir && !bookNamesOnBookShelf.contains(it.name)) { if (!it.isDir && !isOnBookShelf(it)) {
checkableCount++ checkableCount++
} }
} }
@@ -108,7 +123,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
fun selectAll(selectAll: Boolean) { fun selectAll(selectAll: Boolean) {
if (selectAll) { if (selectAll) {
getItems().forEach { getItems().forEach {
if (!it.isDir && !bookNamesOnBookShelf.contains(it.name)) { if (!it.isDir && !isOnBookShelf(it)) {
selectedUris.add(it.toString()) selectedUris.add(it.toString())
} }
} }
@@ -121,7 +136,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
fun revertSelection() { fun revertSelection() {
getItems().forEach { getItems().forEach {
if (!it.isDir && !bookNamesOnBookShelf.contains(it.name)) { if (!it.isDir && !isOnBookShelf(it)) {
if (selectedUris.contains(it.toString())) { if (selectedUris.contains(it.toString())) {
selectedUris.remove(it.toString()) selectedUris.remove(it.toString())
} else { } else {
@@ -144,7 +159,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
interface CallBack { interface CallBack {
fun nextDoc(fileDoc: FileDoc) fun nextDoc(fileDoc: FileDoc)
fun upCountView() fun upCountView()
fun startRead(bookUrl: String) fun startRead(fileDoc: FileDoc)
} }
} }