diff --git a/app/src/main/java/io/legado/app/constant/EventBus.kt b/app/src/main/java/io/legado/app/constant/EventBus.kt index 18be386c5..df3d6017a 100644 --- a/app/src/main/java/io/legado/app/constant/EventBus.kt +++ b/app/src/main/java/io/legado/app/constant/EventBus.kt @@ -28,4 +28,5 @@ object EventBus { const val TIP_COLOR = "tipColor" const val SOURCE_CHANGED = "sourceChanged" const val SEARCH_RESULT = "searchResult" + const val BOOK_URL_CHANGED = "bookUrlChanged" } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index 68b95c9e3..0cac6f128 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -94,13 +94,11 @@ object LocalBook { */ fun importFileOnLine( str: String, - fileName: String? = null, + fileName: String, source: BaseSource? = null, - onLineBook: Book? = null ): Book { - val fileUri = saveBookFile(str, fileName, source, onLineBook) - return importFile(fileUri).let { - mergeBook(it, onLineBook) + return saveBookFile(str, fileName, source).let { + importFile(it) } } @@ -205,41 +203,32 @@ object LocalBook { /** * 下载在线的文件 - * fileName为空时 传入onLineBook */ fun saveBookFile( str: String, - fileName: String? = null, + fileName: String, source: BaseSource? = null, - onLineBook: Book? = null ): Uri { val bytes = when { str.isAbsUrl() -> AnalyzeUrl(str, source = source).getByteArray() str.isDataUrl() -> Base64.decode(str.substringAfter("base64,"), Base64.DEFAULT) else -> throw NoStackTraceException("在线导入书籍支持http/https/DataURL") } - val mFileName = fileName ?: extractDownloadName(str, onLineBook) - return saveBookFile(bytes, mFileName) + return saveBookFile(bytes, fileName) } /** - * 分析下载文件类书源的下载链接 + * 分析下载文件类书源的下载链接的文件后缀 * https://www.example.com/download/{fileName}.{type} 含有文件名和后缀 - * https://www.example.com/download/?fileid=1234, {type: "txt"} + * https://www.example.com/download/?fileid=1234, {type: "txt"} 规则设置 */ - fun extractDownloadName(uri: String, onLineBook: Book?): String { - val analyzeUrl = AnalyzeUrl(uri) + fun parseFileSuffix(url: String): String { + val analyzeUrl = AnalyzeUrl(url) val urlNoOption = analyzeUrl.url val lastPath = urlNoOption.substringAfterLast("/") val fileType = lastPath.substringAfterLast(".") val type = analyzeUrl.type - val fileName = when { - onLineBook != null -> "${onLineBook.name}_${onLineBook.author}_${onLineBook.origin}" - type == null-> lastPath - else -> lastPath.substringBeforeLast(".") - } - val fileSuffix = fileType ?: type ?: "unknown" - return "${fileName}.${fileSuffix}" + return type ?: fileType ?: "unknown" } private fun saveBookFile( diff --git a/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileDialog.kt b/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileDialog.kt index bfe435c0d..667be6b10 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileDialog.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileDialog.kt @@ -1,6 +1,5 @@ package io.legado.app.ui.association -import android.annotation.SuppressLint import android.content.Context import android.content.DialogInterface import android.os.Bundle @@ -21,7 +20,6 @@ import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.widget.dialog.WaitDialog import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding -import splitties.views.onClick /** @@ -39,61 +37,50 @@ class ImportOnLineBookFileDialog() : BaseDialogFragment(R.layout.dialog_recycler setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) } - @SuppressLint("NotifyDataSetChanged") override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { val bookUrl = arguments?.getString("bookUrl") - val infoHtml = arguments?.getString("infoHtml") - viewModel.initData(bookUrl, infoHtml) + viewModel.initData(bookUrl) binding.toolBar.setBackgroundColor(primaryColor) - //标题 - binding.toolBar.setTitle("导入在线书籍文件") + binding.toolBar.setTitle(R.string.download_and_import_file) binding.rotateLoading.show() binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) binding.recyclerView.adapter = adapter - binding.tvCancel.visible() - binding.tvCancel.setOnClickListener { - dismissAllowingStateLoss() - } - binding.tvOk.visible() - binding.tvOk.setOnClickListener { - val waitDialog = WaitDialog(requireContext()) - waitDialog.show() - viewModel.importSelect { - waitDialog.dismiss() - dismissAllowingStateLoss() + viewModel.errorLiveData.observe(this) { + binding.rotateLoading.hide() + binding.tvMsg.apply { + text = it + visible() } } - binding.tvFooterLeft.visible() - binding.tvFooterLeft.setOnClickListener { - val selectAll = viewModel.isSelectAll - viewModel.selectStatus.forEachIndexed { index, b -> - if (b != !selectAll) { - viewModel.selectStatus[index] = !selectAll - } + viewModel.successLiveData.observe(this) { + binding.rotateLoading.hide() + if (it > 0) { + adapter.setItems(viewModel.allBookFiles) } - adapter.notifyDataSetChanged() - upSelectText() } - adapter.setItems(viewModel.allBookFiles) - upSelectText() + viewModel.savedFileUriData.observe(this) { + requireContext().openFileUri(it, "*/*") + } } - private fun upSelectText() { - if (viewModel.isSelectAll) { - binding.tvFooterLeft.text = getString( - R.string.select_cancel_count, - viewModel.selectCount, - viewModel.allBookFiles.size - ) - } else { - binding.tvFooterLeft.text = getString( - R.string.select_all_count, - viewModel.selectCount, - viewModel.allBookFiles.size - ) + private fun importFileAndUpdate(url: String, fileName: String) { + val waitDialog = WaitDialog(requireContext()) + waitDialog.show() + viewModel.importOnLineBookFile(url, fileName) { + waitDialog.dismiss() + dismissAllowingStateLoss() } } + private fun downloadFile(url: String, fileName: String) { + val waitDialog = WaitDialog(requireContext()) + waitDialog.show() + viewModel.downloadUrl(url, fileName) { + waitDialog.dismiss() + dismissAllowingStateLoss() + } +} + inner class BookFileAdapter(context: Context) : RecyclerAdapter , ItemBookFileImportBinding>(context) { @@ -109,39 +96,32 @@ class ImportOnLineBookFileDialog() : BaseDialogFragment(R.layout.dialog_recycler payloads: MutableList ) { binding.apply { - cbFileName.isChecked = viewModel.selectStatus[holder.layoutPosition] cbFileName.text = item.second - if (item.third) { - tvOpen.invisible() - } else { - tvOpen.visible() - } } } - override fun registerListener(holder: ItemViewHolder, binding: ItemBookFileImportBinding) { + override fun registerListener( + holder: ItemViewHolder, + binding: ItemBookFileImportBinding + ) { binding.apply { - cbFileName.setOnCheckedChangeListener { buttonView, isChecked -> - if (buttonView.isPressed) { - val selectFile = viewModel.allBookFiles[holder.layoutPosition] - if (selectFile.third) { - viewModel.selectStatus[holder.layoutPosition] = isChecked - } else { - toastOnUi("不支持直接导入") + cbFileName.setOnClickListener { + val selectFile = viewModel.allBookFiles[holder.layoutPosition] + if (selectFile.third) { + importFileAndUpdate(selectFile.first, selectFile.second) + } else { + alert( + title = getString(R.string.draw), + message = getString(R.string.file_not_supported, selectFile.second) + ) { + okButton { + importFileAndUpdate(selectFile.first, selectFile.second) + } + neutralButton(R.string.open_fun) { + downloadFile(selectFile.first, selectFile.second) + } + cancelButton() } - upSelectText() - } - } - root.onClick { - cbFileName.isChecked = !cbFileName.isChecked - viewModel.selectStatus[holder.layoutPosition] = cbFileName.isChecked - upSelectText() - } - tvOpen.setOnClickListener { - val bookFile = viewModel.allBookFiles[holder.layoutPosition] - //intent解压 - viewModel.downloadUrl(bookFile.first, bookFile.second) { - //openFileUri(it) } } } diff --git a/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileViewModel.kt b/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileViewModel.kt index a05c34e93..bfa3370e9 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportOnLineBookFileViewModel.kt @@ -7,6 +7,7 @@ import io.legado.app.R import io.legado.app.base.BaseViewModel import io.legado.app.constant.AppPattern import io.legado.app.constant.AppLog +import io.legado.app.constant.EventBus import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.exception.NoStackTraceException @@ -18,9 +19,11 @@ import io.legado.app.utils.* class ImportOnLineBookFileViewModel(app: Application) : BaseViewModel(app) { val allBookFiles = arrayListOf>() - val selectStatus = arrayListOf() + val errorLiveData = MutableLiveData() + val successLiveData = MutableLiveData() + val savedFileUriData = MutableLiveData() - fun initData(bookUrl: String?, infoHtml: String?) { + fun initData(bookUrl: String?) { execute { bookUrl ?: throw NoStackTraceException("书籍详情页链接为空") val book = appDb.searchBookDao.getSearchBook(bookUrl)?.toBook() @@ -28,85 +31,48 @@ class ImportOnLineBookFileViewModel(app: Application) : BaseViewModel(app) { val bookSource = appDb.bookSourceDao.getBookSource(book.origin) ?: throw NoStackTraceException("bookSource is null") val ruleDownloadUrls = bookSource?.getBookInfoRule()?.downloadUrls - var content = infoHtml - if (content.isNullOrBlank()) { - content = AnalyzeUrl(bookUrl, source = bookSource).getStrResponse().body - } + val content = AnalyzeUrl(bookUrl, source = bookSource).getStrResponse().body val analyzeRule = AnalyzeRule(book, bookSource) analyzeRule.setContent(content).setBaseUrl(bookUrl) + val fileName = "${book.name} 作者:${book.author}" analyzeRule.getStringList(ruleDownloadUrls, isUrl = true)?.let { it.forEach { url -> - val fileName = LocalBook.extractDownloadName(url, book) val isSupportedFile = AppPattern.bookFileRegex.matches(fileName) - allBookFiles.add(Triple(url, fileName, isSupportedFile)) - selectStatus.add(isSupportedFile) + val mFileName = "${fileName}.${LocalBook.parseFileSuffix(url)}" + allBookFiles.add(Triple(url, mFileName, isSupportedFile)) } } ?: throw NoStackTraceException("下载链接规则解析为空") + }.onSuccess { + successLiveData.postValue(allBookFiles.size) }.onError { + errorLiveData.postValue(it.localizedMessage ?: "") context.toastOnUi("获取书籍下载链接失败\n${it.localizedMessage}") } } - val isSelectAll: Boolean - get() { - selectStatus.forEach { - if (!it) { - return false - } - } - return true - } - - val selectCount: Int - get() { - var count = 0 - selectStatus.forEach { - if (it) { - count++ - } - } - return count - } - - fun importSelect(success: () -> Unit) { + fun downloadUrl(url: String, fileName: String, success: () -> Unit) { execute { - selectStatus.forEachIndexed { index, selected -> - if (selected) { - val selectedFile = allBookFiles[index] - val isSupportedFile = selectedFile.third - val fileUrl: String = selectedFile.first - val fileName: String = selectedFile.second - when { - isSupportedFile -> importOnLineBookFile(fileUrl, fileName) - else -> { - downloadUrl(fileUrl, fileName) { - // AppLog.putDebug("下载文件路径: ${it.toString()}") - } - } - } - } + LocalBook.saveBookFile(url, fileName).let { + savedFileUriData.postValue(it) } }.onSuccess { success.invoke() }.onError { - + context.toastOnUi("下载书籍文件失败\n${it.localizedMessage}") } } - - fun downloadUrl(url: String, fileName: String, success: () -> Unit) { + fun importOnLineBookFile(url: String, fileName: String, success: () -> Unit) { execute { - LocalBook.saveBookFile(url, fileName) + LocalBook.importFileOnLine(url, fileName).let { + postEvent(EventBus.BOOK_URL_CHANGED, it.bookUrl) + } }.onSuccess { - success.invoke() + success.invoke() }.onError { context.toastOnUi("下载书籍文件失败\n${it.localizedMessage}") } } - fun importOnLineBookFile(url: String, fileName: String) { - LocalBook.importFileOnLine(url, fileName) - } - } diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index f94345da5..56e2bd5d7 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -12,6 +12,7 @@ import androidx.activity.viewModels import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.constant.BookType +import io.legado.app.constant.EventBus import io.legado.app.constant.Theme import io.legado.app.data.appDb import io.legado.app.data.entities.Book @@ -283,7 +284,6 @@ class BookInfoActivity : if (viewModel.isImportBookOnLine) { showDialogFragment { putString("bookUrl", book.bookUrl) - putString("infoHtml", book.infoHtml) } } else { readBook(book) @@ -490,4 +490,9 @@ class BookInfoActivity : } } + override fun observeLiveBus() { + observeEvent(EventBus.BOOK_URL_CHANGED) { + viewModel.changeToLocalBook(it) + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt index f8506ece9..8303d8357 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt @@ -119,6 +119,9 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { WebBook.getBookInfo(this, bookSource, book, canReName = canReName) .onSuccess(IO) { bookData.postValue(book) + if (isImportBookOnLine) { + appDb.searchBookDao.update(book.toSearchBook()) + } if (inBookshelf) { appDb.bookDao.update(book) } @@ -291,13 +294,17 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { } } - private fun changeToLocalBook(book: Book) { - bookData.postValue(book) - LocalBook.getChapterList(book).let { - chapterListData.postValue(it) + fun changeToLocalBook(bookUrl: String) { + appDb.bookDao.getBook(bookUrl)?.let { localBook -> + LocalBook.mergeBook(localBook, bookData.value).let { + bookData.postValue(it) + } + LocalBook.getChapterList(localBook).let { + chapterListData.postValue(it) + } + isImportBookOnLine = false + inBookshelf = true } - isImportBookOnLine = false - inBookshelf = true } } \ No newline at end of file diff --git a/app/src/main/res/layout/item_book_file_import.xml b/app/src/main/res/layout/item_book_file_import.xml index 88291196c..8f584c1a4 100644 --- a/app/src/main/res/layout/item_book_file_import.xml +++ b/app/src/main/res/layout/item_book_file_import.xml @@ -6,11 +6,12 @@ android:layout_height="wrap_content" android:padding="8dp"> - - - - - \ No newline at end of file + diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 030c5335e..741265978 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -981,5 +981,7 @@ CookieJar 点击阅读加载目录 清除cookie + 导入在线书籍文件 + diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 1cfcb0d47..e24e9b362 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -984,5 +984,7 @@ CookieJar 点击阅读加载目录 清除cookie + 导入在线书籍文件 + diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 1293cedc9..95fb2ec20 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -984,5 +984,7 @@ CookieJar 点击阅读加载目录 清除cookie + 导入在线书籍文件 + diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 94805b80d..3b97eaf2b 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -981,5 +981,7 @@ CookieJar 点击阅读加载目录 清除cookie + 导入在线书籍文件 + diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 7dea2e10e..975d7dbba 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -983,5 +983,7 @@ CookieJar 点击阅读加载目录 清除cookie + 导入在线书籍文件 + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index b20df127c..018ccb9d8 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -983,5 +983,6 @@ CookieJar 点击阅读加载目录 清除cookie + 导入在线书籍文件 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6799fca04..d582253cd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -984,5 +984,7 @@ CookieJar 点击阅读加载目录 清除cookie + 导入在线书籍文件 +