Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -44,9 +44,6 @@ interface BookDao {
|
|||||||
)
|
)
|
||||||
fun flowLocalNoGroup(): Flow<List<Book>>
|
fun flowLocalNoGroup(): Flow<List<Book>>
|
||||||
|
|
||||||
@Query("SELECT bookUrl FROM books WHERE type & ${BookType.local} > 0")
|
|
||||||
fun flowLocalUri(): Flow<List<String>>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM books WHERE (`group` & :group) > 0")
|
@Query("SELECT * FROM books WHERE (`group` & :group) > 0")
|
||||||
fun flowByGroup(group: Long): Flow<List<Book>>
|
fun flowByGroup(group: Long): Flow<List<Book>>
|
||||||
|
|
||||||
@@ -104,7 +101,7 @@ interface BookDao {
|
|||||||
@Query("select 1 from books where bookUrl = :bookUrl")
|
@Query("select 1 from books where bookUrl = :bookUrl")
|
||||||
fun has(bookUrl: String): Boolean?
|
fun has(bookUrl: String): Boolean?
|
||||||
|
|
||||||
@Query("select 1 from books where originName = :fileName")
|
@Query("select 1 from books where originName = :fileName or origin like '%' || :fileName")
|
||||||
fun hasFile(fileName: String): Boolean?
|
fun hasFile(fileName: String): Boolean?
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ val Book.archiveName: String?
|
|||||||
get() {
|
get() {
|
||||||
return if (isArchive) {
|
return if (isArchive) {
|
||||||
// local_book::archive.rar
|
// local_book::archive.rar
|
||||||
origin.substring(BookType.localTag.length + 2)
|
// webDav::https://...../archive.rar
|
||||||
|
origin.substringAfter("::").substringAfterLast("/")
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,21 @@ object LocalBook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 批量导入 支持自动导入压缩包的支持书籍 */
|
/* 批量导入 支持自动导入压缩包的支持书籍 */
|
||||||
|
fun importFiles(uri: Uri): List<Book> {
|
||||||
|
val books = mutableListOf<Book>()
|
||||||
|
val fileDoc = FileDoc.fromUri(uri, false)
|
||||||
|
if (ArchiveUtils.isArchive(fileDoc.name)) {
|
||||||
|
books.addAll(
|
||||||
|
importArchiveFile(uri) {
|
||||||
|
it.matches(AppPattern.bookFileRegex)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
books.add(importFile(uri))
|
||||||
|
}
|
||||||
|
return books
|
||||||
|
}
|
||||||
|
|
||||||
fun importFiles(uris: List<Uri>) {
|
fun importFiles(uris: List<Uri>) {
|
||||||
var errorCount = 0
|
var errorCount = 0
|
||||||
uris.forEach { uri ->
|
uris.forEach { uri ->
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.legado.app.model.remote
|
package io.legado.app.model.remote
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import io.legado.app.constant.AppPattern.archiveFileRegex
|
||||||
import io.legado.app.constant.AppPattern.bookFileRegex
|
import io.legado.app.constant.AppPattern.bookFileRegex
|
||||||
import io.legado.app.constant.BookType
|
import io.legado.app.constant.BookType
|
||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
@@ -39,7 +40,8 @@ class RemoteBookWebDav(
|
|||||||
val remoteWebDavFileList: List<WebDavFile> = WebDav(path, authorization).listFiles()
|
val remoteWebDavFileList: List<WebDavFile> = WebDav(path, authorization).listFiles()
|
||||||
//转化远程文件信息到本地对象
|
//转化远程文件信息到本地对象
|
||||||
remoteWebDavFileList.forEach { webDavFile ->
|
remoteWebDavFileList.forEach { webDavFile ->
|
||||||
if (webDavFile.isDir || bookFileRegex.matches(webDavFile.displayName)) {
|
if (webDavFile.isDir || bookFileRegex.matches(webDavFile.displayName) || archiveFileRegex.matches(webDavFile.displayName)
|
||||||
|
) {
|
||||||
//扩展名符合阅读的格式则认为是书籍
|
//扩展名符合阅读的格式则认为是书籍
|
||||||
remoteBooks.add(RemoteBook(webDavFile))
|
remoteBooks.add(RemoteBook(webDavFile))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,15 +98,16 @@ abstract class BaseImportBookActivity<VM : ViewModel> : VMBaseActivity<ActivityI
|
|||||||
it.matches(AppPattern.bookFileRegex)
|
it.matches(AppPattern.bookFileRegex)
|
||||||
}
|
}
|
||||||
if (fileNames.size == 1) {
|
if (fileNames.size == 1) {
|
||||||
appDb.bookDao.getBookByFileName(fileNames[0])?.let {
|
val name = fileNames[0]
|
||||||
|
appDb.bookDao.getBookByFileName(name)?.let {
|
||||||
startReadBook(it.bookUrl)
|
startReadBook(it.bookUrl)
|
||||||
} ?: toastOnUi(R.string.no_book_found_bookshelf)
|
} ?: showImportAlert(fileDoc, name)
|
||||||
} else {
|
} else {
|
||||||
showSelectBookReadAlert(fileNames)
|
showSelectBookReadAlert(fileDoc, fileNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSelectBookReadAlert(fileNames: List<String>) {
|
private fun showSelectBookReadAlert(fileDoc: FileDoc, fileNames: List<String>) {
|
||||||
if (fileNames.isEmpty()) {
|
if (fileNames.isEmpty()) {
|
||||||
toastOnUi(R.string.unsupport_archivefile_entry)
|
toastOnUi(R.string.unsupport_archivefile_entry)
|
||||||
return
|
return
|
||||||
@@ -117,7 +118,29 @@ abstract class BaseImportBookActivity<VM : ViewModel> : VMBaseActivity<ActivityI
|
|||||||
) { _, name, _ ->
|
) { _, name, _ ->
|
||||||
appDb.bookDao.getBookByFileName(name)?.let {
|
appDb.bookDao.getBookByFileName(name)?.let {
|
||||||
startReadBook(it.bookUrl)
|
startReadBook(it.bookUrl)
|
||||||
} ?: toastOnUi(R.string.no_book_found_bookshelf)
|
} ?: showImportAlert(fileDoc, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun addArchiveToBookShelf(
|
||||||
|
fileDoc: FileDoc,
|
||||||
|
fileName: String,
|
||||||
|
onSuccess: (String) -> Unit
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 提示是否重新导入所点击的压缩文件 */
|
||||||
|
private fun showImportAlert(fileDoc: FileDoc, fileName: String) {
|
||||||
|
alert(
|
||||||
|
R.string.draw,
|
||||||
|
R.string.no_book_found_bookshelf
|
||||||
|
) {
|
||||||
|
okButton {
|
||||||
|
addArchiveToBookShelf(fileDoc, fileName) {
|
||||||
|
startReadBook(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,11 +131,6 @@ class ImportBookActivity : BaseImportBookActivity<ImportBookViewModel>(),
|
|||||||
viewModel.dataFlowStart = {
|
viewModel.dataFlowStart = {
|
||||||
initRootDoc()
|
initRootDoc()
|
||||||
}
|
}
|
||||||
launch {
|
|
||||||
appDb.bookDao.flowLocal().conflate().collect {
|
|
||||||
adapter.upBookHas(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
launch {
|
||||||
viewModel.dataFlow.conflate().collect { docs ->
|
viewModel.dataFlow.conflate().collect { docs ->
|
||||||
adapter.setItems(docs)
|
adapter.setItems(docs)
|
||||||
@@ -308,4 +303,11 @@ class ImportBookActivity : BaseImportBookActivity<ImportBookViewModel>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addArchiveToBookShelf(
|
||||||
|
fileDoc: FileDoc,
|
||||||
|
fileName: String,
|
||||||
|
onSuccess: (String) -> Unit
|
||||||
|
) {
|
||||||
|
viewModel.addArchiveToBookShelf(fileDoc, fileName, onSuccess)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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.archiveName
|
||||||
import io.legado.app.help.book.isArchive
|
import io.legado.app.help.book.isArchive
|
||||||
|
import io.legado.app.model.localBook.LocalBook
|
||||||
import io.legado.app.utils.*
|
import io.legado.app.utils.*
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +20,6 @@ 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 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)
|
||||||
@@ -87,26 +87,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isOnBookShelf(fileDoc: FileDoc): Boolean {
|
private fun isOnBookShelf(fileDoc: FileDoc): Boolean {
|
||||||
return bookOrArchiveNamesOnBookShelf.contains(fileDoc.name)
|
return LocalBook.isOnBookShelf(fileDoc.name)
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
fun upBookHas(books: List<Book>) {
|
|
||||||
bookOrArchiveNamesOnBookShelf.clear()
|
|
||||||
books.forEach {
|
|
||||||
if (it.isArchive) {
|
|
||||||
bookOrArchiveNamesOnBookShelf.add(
|
|
||||||
it.archiveName!!
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val path = Uri.decode(it.bookUrl)
|
|
||||||
bookOrArchiveNamesOnBookShelf.add(
|
|
||||||
FileUtils.getName(path)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notifyDataSetChanged()
|
|
||||||
upCheckableCount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun upCheckableCount() {
|
private fun upCheckableCount() {
|
||||||
|
|||||||
@@ -97,6 +97,18 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addArchiveToBookShelf(
|
||||||
|
fileDoc: FileDoc,
|
||||||
|
fileName: String,
|
||||||
|
onSuccess: (String) -> Unit
|
||||||
|
) {
|
||||||
|
LocalBook.importArchiveFile(fileDoc.uri, fileName) {
|
||||||
|
it.contains(fileName)
|
||||||
|
}.firstOrNull()?.run {
|
||||||
|
onSuccess.invoke(bookUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun deleteDoc(uriList: HashSet<String>, finally: () -> Unit) {
|
fun deleteDoc(uriList: HashSet<String>, finally: () -> Unit) {
|
||||||
execute {
|
execute {
|
||||||
uriList.forEach {
|
uriList.forEach {
|
||||||
|
|||||||
@@ -124,18 +124,17 @@ class RemoteBookViewModel(application: Application) : BaseViewModel(application)
|
|||||||
|
|
||||||
fun addToBookshelf(remoteBooks: HashSet<RemoteBook>, finally: () -> Unit) {
|
fun addToBookshelf(remoteBooks: HashSet<RemoteBook>, finally: () -> Unit) {
|
||||||
execute {
|
execute {
|
||||||
remoteBooks.forEach { remoteBook ->
|
|
||||||
val bookWebDav = remoteBookWebDav
|
val bookWebDav = remoteBookWebDav
|
||||||
?: throw NoStackTraceException("没有配置webDav")
|
?: throw NoStackTraceException("没有配置webDav")
|
||||||
val downloadBookPath = bookWebDav.downloadRemoteBook(remoteBook)
|
remoteBooks.forEach { remoteBook ->
|
||||||
downloadBookPath.let {
|
val downloadBookUri = bookWebDav.downloadRemoteBook(remoteBook)
|
||||||
val localBook = LocalBook.importFile(it)
|
LocalBook.importFiles(downloadBookUri).forEach { book ->
|
||||||
localBook.origin = BookType.webDavTag + CustomUrl(remoteBook.path)
|
book.origin = BookType.webDavTag + CustomUrl(remoteBook.path)
|
||||||
.putAttribute(
|
.putAttribute(
|
||||||
"serverID",
|
"serverID",
|
||||||
bookWebDav.serverID
|
bookWebDav.serverID
|
||||||
).toString()
|
).toString()
|
||||||
localBook.save()
|
book.save()
|
||||||
remoteBook.isOnBookShelf = true
|
remoteBook.isOnBookShelf = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,21 @@ object UrlUtil {
|
|||||||
conn.instanceFollowRedirects = false
|
conn.instanceFollowRedirects = false
|
||||||
conn.connect()
|
conn.connect()
|
||||||
|
|
||||||
|
if (AppConfig.recordLog || BuildConfig.DEBUG) {
|
||||||
|
val headers = conn.headerFields
|
||||||
|
val headersString = buildString {
|
||||||
|
headers.forEach { (key, value) ->
|
||||||
|
value.forEach {
|
||||||
|
append(key)
|
||||||
|
append(": ")
|
||||||
|
append(it)
|
||||||
|
append("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppLog.put("${url.toString()} response header:\n$headersString")
|
||||||
|
}
|
||||||
|
|
||||||
// val fileSize = conn.getContentLengthLong() / 1024
|
// val fileSize = conn.getContentLengthLong() / 1024
|
||||||
/** Content-Disposition 存在三种情况 文件名应该用引号 有些用空格
|
/** Content-Disposition 存在三种情况 文件名应该用引号 有些用空格
|
||||||
* filename="filename"
|
* filename="filename"
|
||||||
@@ -114,21 +129,7 @@ object UrlUtil {
|
|||||||
val newUrl= URL(URLDecoder.decode(redirectUrl, "UTF-8"))
|
val newUrl= URL(URLDecoder.decode(redirectUrl, "UTF-8"))
|
||||||
getFileNameFromPath(newUrl)
|
getFileNameFromPath(newUrl)
|
||||||
} else {
|
} else {
|
||||||
AppLog.put("Cannot obtain URL file name, enable recordLog for detail")
|
AppLog.put("Cannot obtain URL file name, enable recordLog for response header")
|
||||||
if (AppConfig.recordLog || BuildConfig.DEBUG) {
|
|
||||||
val headers = conn.headerFields
|
|
||||||
val headersString = buildString {
|
|
||||||
headers.forEach { (key, value) ->
|
|
||||||
value.forEach {
|
|
||||||
append(key)
|
|
||||||
append(": ")
|
|
||||||
append(it)
|
|
||||||
append("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppLog.put("${url.toString()} response header:\n$headersString")
|
|
||||||
}
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1083,5 +1083,5 @@
|
|||||||
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
||||||
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
||||||
<string name="delete_alert">删除提醒</string>
|
<string name="delete_alert">删除提醒</string>
|
||||||
<string name="no_book_found_bookshelf">No book found in bookshelf</string>
|
<string name="no_book_found_bookshelf">No book found in bookshelf, import again ?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1086,5 +1086,5 @@
|
|||||||
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
||||||
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
||||||
<string name="delete_alert">删除提醒</string>
|
<string name="delete_alert">删除提醒</string>
|
||||||
<string name="no_book_found_bookshelf">No book found in bookshelf</string>
|
<string name="no_book_found_bookshelf">No book found in bookshelf, import again ?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1086,5 +1086,5 @@
|
|||||||
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
||||||
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
||||||
<string name="delete_alert">删除提醒</string>
|
<string name="delete_alert">删除提醒</string>
|
||||||
<string name="no_book_found_bookshelf">No book found in bookshelf</string>
|
<string name="no_book_found_bookshelf">No book found in bookshelf, import again ?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1083,5 +1083,5 @@
|
|||||||
<string name="unsupport_archivefile_entry">压缩文件内没有支持的文件</string>
|
<string name="unsupport_archivefile_entry">压缩文件内没有支持的文件</string>
|
||||||
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
||||||
<string name="delete_alert">删除提醒</string>
|
<string name="delete_alert">删除提醒</string>
|
||||||
<string name="no_book_found_bookshelf">未在书架上找到所选书籍</string>
|
<string name="no_book_found_bookshelf">未在书架上找到所选书籍, 是否重新导入?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1085,5 +1085,5 @@
|
|||||||
<string name="unsupport_archivefile_entry">压缩文件内没有支持的文件</string>
|
<string name="unsupport_archivefile_entry">压缩文件内没有支持的文件</string>
|
||||||
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
||||||
<string name="delete_alert">删除提醒</string>
|
<string name="delete_alert">删除提醒</string>
|
||||||
<string name="no_book_found_bookshelf">未在书架上找到所选书籍</string>
|
<string name="no_book_found_bookshelf">未在书架上找到所选书籍, 是否重新导入?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1085,5 +1085,5 @@
|
|||||||
<string name="unsupport_archivefile_entry">压缩文件内没有支持的文件</string>
|
<string name="unsupport_archivefile_entry">压缩文件内没有支持的文件</string>
|
||||||
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
||||||
<string name="delete_alert">删除提醒</string>
|
<string name="delete_alert">删除提醒</string>
|
||||||
<string name="no_book_found_bookshelf">未在书架上找到所选书籍</string>
|
<string name="no_book_found_bookshelf">未在书架上找到所选书籍, 是否重新导入?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1086,5 +1086,5 @@
|
|||||||
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
<string name="unsupport_archivefile_entry">Cannot find supported files in archive</string>
|
||||||
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
<string name="no_books_dir">没有设置书籍保存位置!</string>
|
||||||
<string name="delete_alert">删除提醒</string>
|
<string name="delete_alert">删除提醒</string>
|
||||||
<string name="no_book_found_bookshelf">No book found in bookshelf</string>
|
<string name="no_book_found_bookshelf">No book found in bookshelf, import again ?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user