Merge remote-tracking branch 'origin/master' into epublazy

pull/1759/head
ag2s20150909 3 years ago
commit 7ef57c03b9
  1. 2
      app/src/main/AndroidManifest.xml
  2. 6
      app/src/main/assets/updateLog.md
  3. 1
      app/src/main/java/io/legado/app/constant/AppConst.kt
  4. 4
      app/src/main/java/io/legado/app/data/entities/Book.kt
  5. 12
      app/src/main/java/io/legado/app/help/config/AppConfig.kt
  6. 49
      app/src/main/java/io/legado/app/ui/association/FileAssociationViewModel.kt
  7. 2
      app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt
  8. 4
      app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt
  9. 9
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt
  10. 5
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt
  11. 9
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceDialog.kt
  12. 14
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  13. 22
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  14. 4
      app/src/main/java/io/legado/app/ui/book/manage/BookAdapter.kt
  15. 28
      app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageActivity.kt
  16. 5
      app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageViewModel.kt
  17. 2
      app/src/main/java/io/legado/app/ui/book/manage/ComposeView.kt
  18. 2
      app/src/main/java/io/legado/app/ui/book/manage/SourcePickerDialog.kt
  19. 1
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  20. 16
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  21. 4
      app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt
  22. 11
      app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt
  23. 2
      app/src/main/res/layout/activity_arrange_book.xml
  24. 0
      app/src/main/res/menu/bookshelf_manage.xml
  25. 0
      app/src/main/res/menu/bookshelf_menage_sel.xml
  26. 12
      app/src/main/res/menu/main_bookshelf.xml
  27. 2
      app/src/main/res/values-es-rES/strings.xml
  28. 2
      app/src/main/res/values-ja-rJP/strings.xml
  29. 2
      app/src/main/res/values-pt-rBR/strings.xml
  30. 2
      app/src/main/res/values-zh-rHK/strings.xml
  31. 2
      app/src/main/res/values-zh-rTW/strings.xml
  32. 2
      app/src/main/res/values-zh/strings.xml
  33. 2
      app/src/main/res/values/strings.xml

@ -249,7 +249,7 @@
android:screenOrientation="behind" /> android:screenOrientation="behind" />
<!-- 书籍管理 --> <!-- 书籍管理 -->
<activity <activity
android:name=".ui.book.arrange.ArrangeBookActivity" android:name=".ui.book.manage.BookshelfManageActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="behind" /> android:screenOrientation="behind" />
<!-- 书源调试 --> <!-- 书源调试 -->

@ -11,6 +11,12 @@
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
**2022/04/07**
* 实现epub的懒加载 by ag2s20150909
* 解决txt无法导入
* 优化换源
**2022/04/05** **2022/04/05**
* 更新cronet: 100.0.4896.79 * 更新cronet: 100.0.4896.79

@ -39,6 +39,7 @@ object AppConst {
SimpleDateFormat("yy-MM-dd-HH-mm-ss") SimpleDateFormat("yy-MM-dd-HH-mm-ss")
} }
const val rootGroupId = -100L
const val bookGroupAllId = -1L const val bookGroupAllId = -1L
const val bookGroupLocalId = -2L const val bookGroupLocalId = -2L
const val bookGroupAudioId = -3L const val bookGroupAudioId = -3L

@ -287,10 +287,6 @@ data class Book(
newBook.customTag = customTag newBook.customTag = customTag
newBook.canUpdate = canUpdate newBook.canUpdate = canUpdate
newBook.readConfig = readConfig newBook.readConfig = readConfig
if (appDb.bookDao.has(bookUrl) == true) {
delete()
appDb.bookDao.insert(newBook)
}
return newBook return newBook
} }

@ -276,9 +276,17 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val autoChangeSource: Boolean val autoChangeSource: Boolean
get() = appCtx.getPrefBoolean(PreferKey.autoChangeSource, true) get() = appCtx.getPrefBoolean(PreferKey.autoChangeSource, true)
val changeSourceLoadInfo get() = appCtx.getPrefBoolean(PreferKey.changeSourceLoadInfo) var changeSourceLoadInfo: Boolean
get() = appCtx.getPrefBoolean(PreferKey.changeSourceLoadInfo)
set(value) {
appCtx.putPrefBoolean(PreferKey.changeSourceLoadInfo, value)
}
val changeSourceLoadToc get() = appCtx.getPrefBoolean(PreferKey.changeSourceLoadToc) var changeSourceLoadToc: Boolean
get() = appCtx.getPrefBoolean(PreferKey.changeSourceLoadToc)
set(value) {
appCtx.putPrefBoolean(PreferKey.changeSourceLoadToc, value)
}
val importKeepName get() = appCtx.getPrefBoolean(PreferKey.importKeepName) val importKeepName get() = appCtx.getPrefBoolean(PreferKey.importKeepName)

@ -24,34 +24,43 @@ class FileAssociationViewModel(application: Application) : BaseAssociationViewMo
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
fun dispatchIndent(uri: Uri, finally: (title: String, msg: String) -> Unit) { fun dispatchIndent(uri: Uri, finally: (title: String, msg: String) -> Unit) {
execute { execute {
lateinit var fileName: String
lateinit var content: String
//如果是普通的url,需要根据返回的内容判断是什么 //如果是普通的url,需要根据返回的内容判断是什么
if (uri.scheme == "file" || uri.scheme == "content") { if (uri.scheme == "file" || uri.scheme == "content") {
val content = if (uri.scheme == "file") { if (uri.scheme == "file") {
File(uri.path.toString()).readText() val file = File(uri.path.toString())
content = file.readText()
fileName = file.name
} else { } else {
DocumentFile.fromSingleUri(context, uri)?.readText(context) val file = DocumentFile.fromSingleUri(context, uri)
} ?: throw NoStackTraceException("文件不存在") content = file?.readText(context) ?: throw NoStackTraceException("文件不存在")
fileName = file.name ?: ""
}
when { when {
content.isJson() -> when { content.isJson() -> {
content.contains("bookSourceUrl") -> //暂时根据文件内容判断属于什么
importBookSourceLive.postValue(content) when {
content.contains("sourceUrl") -> content.contains("bookSourceUrl") ->
importRssSourceLive.postValue(content) importBookSourceLive.postValue(content)
content.contains("pattern") -> content.contains("sourceUrl") ->
importReplaceRuleLive.postValue(content) importRssSourceLive.postValue(content)
content.contains("themeName") -> content.contains("pattern") ->
importTheme(content, finally) importReplaceRuleLive.postValue(content)
content.contains("name") && content.contains("rule") -> content.contains("themeName") ->
importTextTocRule(content, finally) importTheme(content, finally)
content.contains("name") && content.contains("url") -> content.contains("name") && content.contains("rule") ->
importHttpTTS(content, finally) importTextTocRule(content, finally)
else -> errorLiveData.postValue("格式不对") content.contains("name") && content.contains("url") ->
importHttpTTS(content, finally)
else -> errorLiveData.postValue("格式不对")
}
} }
(uri.path ?: uri.toString()).matches(bookFileRegex) -> { fileName.matches(bookFileRegex) -> {
importBookLiveData.postValue(uri) importBookLiveData.postValue(uri)
} }
else -> { else -> {
throw NoStackTraceException("暂未支持的本地书籍格式(TXT/UMD/EPUB)") throw NoStackTraceException("$fileName 暂未支持的本地书籍格式(TXT/UMD/EPUB)")
} }
} }
} else { } else {

@ -16,6 +16,7 @@ import io.legado.app.constant.BookType
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.Status import io.legado.app.constant.Status
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
@ -212,6 +213,7 @@ class AudioPlayActivity :
launch { launch {
withContext(IO) { withContext(IO) {
AudioPlay.book?.changeTo(book, toc) AudioPlay.book?.changeTo(book, toc)
appDb.bookDao.insert(book)
} }
startActivity<ReadBookActivity> { startActivity<ReadBookActivity> {
putExtra("bookUrl", book.bookUrl) putExtra("bookUrl", book.bookUrl)

@ -78,7 +78,9 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) { fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
execute { execute {
AudioPlay.book = AudioPlay.book!!.changeTo(book, toc) AudioPlay.book?.changeTo(book, toc)
appDb.bookDao.insert(book)
AudioPlay.book = book
AudioPlay.bookSource = source AudioPlay.bookSource = source
appDb.bookChapterDao.insert(*toc.toTypedArray()) appDb.bookChapterDao.insert(*toc.toTypedArray())
AudioPlay.upDurChapter(book) AudioPlay.upDurChapter(book)

@ -16,7 +16,6 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
@ -207,12 +206,12 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
viewModel.refresh() viewModel.refresh()
} }
R.id.menu_load_toc -> { R.id.menu_load_info -> {
putPrefBoolean(PreferKey.changeSourceLoadToc, !item.isChecked) AppConfig.changeSourceLoadInfo = !item.isChecked
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
} }
R.id.menu_load_info -> { R.id.menu_load_toc -> {
putPrefBoolean(PreferKey.changeSourceLoadInfo, !item.isChecked) AppConfig.changeSourceLoadToc = !item.isChecked
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
} }
R.id.menu_start_stop -> viewModel.startOrStopSearch() R.id.menu_start_stop -> viewModel.startOrStopSearch()

@ -184,12 +184,11 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
} }
private suspend fun loadBookInfo(scope: CoroutineScope, source: BookSource, book: Book) { private suspend fun loadBookInfo(scope: CoroutineScope, source: BookSource, book: Book) {
val mBook = WebBook.getBookInfoAwait(scope, source, book) WebBook.getBookInfoAwait(scope, source, book)
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) { if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadBookToc(scope, source, mBook) loadBookToc(scope, source, book)
} else { } else {
//从详情页里获取最新章节 //从详情页里获取最新章节
book.latestChapterTitle = mBook.latestChapterTitle
val searchBook = book.toSearchBook() val searchBook = book.toSearchBook()
searchCallback?.searchSuccess(searchBook) searchCallback?.searchSuccess(searchBook)
} }

@ -16,7 +16,6 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
@ -231,12 +230,12 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
viewModel.refresh() viewModel.refresh()
} }
R.id.menu_load_toc -> { R.id.menu_load_info -> {
putPrefBoolean(PreferKey.changeSourceLoadToc, !item.isChecked) AppConfig.changeSourceLoadInfo = !item.isChecked
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
} }
R.id.menu_load_info -> { R.id.menu_load_toc -> {
putPrefBoolean(PreferKey.changeSourceLoadInfo, !item.isChecked) AppConfig.changeSourceLoadToc = !item.isChecked
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
} }
R.id.menu_start_stop -> viewModel.startOrStopSearch() R.id.menu_start_stop -> viewModel.startOrStopSearch()

@ -270,7 +270,7 @@ class BookInfoActivity :
showDialogFragment( showDialogFragment(
ChangeCoverDialog(it.name, it.author) ChangeCoverDialog(it.name, it.author)
) )
} } ?: toastOnUi("Book is null")
} }
ivCover.setOnLongClickListener { ivCover.setOnLongClickListener {
viewModel.bookData.value?.getDisplayCover()?.let { path -> viewModel.bookData.value?.getDisplayCover()?.let { path ->
@ -281,7 +281,7 @@ class BookInfoActivity :
tvRead.setOnClickListener { tvRead.setOnClickListener {
viewModel.bookData.value?.let { viewModel.bookData.value?.let {
readBook(it) readBook(it)
} } ?: toastOnUi("Book is null")
} }
tvShelf.setOnClickListener { tvShelf.setOnClickListener {
if (viewModel.inBookshelf) { if (viewModel.inBookshelf) {
@ -297,12 +297,12 @@ class BookInfoActivity :
startActivity<BookSourceEditActivity> { startActivity<BookSourceEditActivity> {
putExtra("sourceUrl", it.origin) putExtra("sourceUrl", it.origin)
} }
} } ?: toastOnUi("Book is null")
} }
tvChangeSource.setOnClickListener { tvChangeSource.setOnClickListener {
viewModel.bookData.value?.let { viewModel.bookData.value?.let { book ->
showDialogFragment(ChangeBookSourceDialog(it.name, it.author)) showDialogFragment(ChangeBookSourceDialog(book.name, book.author))
} } ?: toastOnUi("Book is null")
} }
tvTocView.setOnClickListener { tvTocView.setOnClickListener {
if (!viewModel.inBookshelf) { if (!viewModel.inBookshelf) {
@ -320,7 +320,7 @@ class BookInfoActivity :
showDialogFragment( showDialogFragment(
GroupSelectDialog(it.group) GroupSelectDialog(it.group)
) )
} } ?: toastOnUi("Book is null")
} }
tvAuthor.setOnClickListener { tvAuthor.setOnClickListener {
startActivity<SearchActivity> { startActivity<SearchActivity> {

@ -39,17 +39,17 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
val bookUrl = intent.getStringExtra("bookUrl") ?: "" val bookUrl = intent.getStringExtra("bookUrl") ?: ""
appDb.bookDao.getBook(name, author)?.let { appDb.bookDao.getBook(name, author)?.let {
inBookshelf = true inBookshelf = true
setBook(it) upBook(it)
return@execute return@execute
} }
if (bookUrl.isNotBlank()) { if (bookUrl.isNotBlank()) {
appDb.searchBookDao.getSearchBook(bookUrl)?.toBook()?.let { appDb.searchBookDao.getSearchBook(bookUrl)?.toBook()?.let {
setBook(it) upBook(it)
return@execute return@execute
} }
} }
appDb.searchBookDao.getFirstByNameAuthor(name, author)?.toBook()?.let { appDb.searchBookDao.getFirstByNameAuthor(name, author)?.toBook()?.let {
setBook(it) upBook(it)
return@execute return@execute
} }
throw NoStackTraceException("未找到书籍") throw NoStackTraceException("未找到书籍")
@ -63,12 +63,12 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
val name = intent.getStringExtra("name") ?: "" val name = intent.getStringExtra("name") ?: ""
val author = intent.getStringExtra("author") ?: "" val author = intent.getStringExtra("author") ?: ""
appDb.bookDao.getBook(name, author)?.let { book -> appDb.bookDao.getBook(name, author)?.let { book ->
setBook(book) upBook(book)
} }
} }
} }
private fun setBook(book: Book) { private fun upBook(book: Book) {
execute { execute {
durChapterIndex = book.durChapterIndex durChapterIndex = book.durChapterIndex
bookData.postValue(book) bookData.postValue(book)
@ -176,15 +176,19 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
} }
} }
fun changeTo(source: BookSource, newBook: Book, toc: List<BookChapter>) { fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
changeSourceCoroutine?.cancel() changeSourceCoroutine?.cancel()
changeSourceCoroutine = execute { changeSourceCoroutine = execute {
bookSource = source bookSource = source
bookData.value!!.changeTo(newBook, toc) bookData.value?.changeTo(book, toc)
bookData.postValue(newBook) if (inBookshelf) {
appDb.bookDao.insert(book)
appDb.bookChapterDao.insert(*toc.toTypedArray())
}
bookData.postValue(book)
chapterListData.postValue(toc) chapterListData.postValue(toc)
}.onFinally { }.onFinally {
postEvent(EventBus.SOURCE_CHANGED, newBook.bookUrl) postEvent(EventBus.SOURCE_CHANGED, book.bookUrl)
} }
} }

@ -1,4 +1,4 @@
package io.legado.app.ui.book.arrange package io.legado.app.ui.book.manage
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
@ -15,7 +15,7 @@ import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
class ArrangeBookAdapter(context: Context, val callBack: CallBack) : class BookAdapter(context: Context, val callBack: CallBack) :
RecyclerAdapter<Book, ItemArrangeBookBinding>(context), RecyclerAdapter<Book, ItemArrangeBookBinding>(context),
ItemTouchCallback.Callback { ItemTouchCallback.Callback {

@ -1,4 +1,4 @@
package io.legado.app.ui.book.arrange package io.legado.app.ui.book.manage
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
@ -39,19 +39,20 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBookViewModel>(), class BookshelfManageActivity :
VMBaseActivity<ActivityArrangeBookBinding, BookshelfManageViewModel>(),
PopupMenu.OnMenuItemClickListener, PopupMenu.OnMenuItemClickListener,
SelectActionBar.CallBack, SelectActionBar.CallBack,
ArrangeBookAdapter.CallBack, BookAdapter.CallBack,
SourcePickerDialog.Callback, SourcePickerDialog.Callback,
GroupSelectDialog.CallBack { GroupSelectDialog.CallBack {
override val binding by viewBinding(ActivityArrangeBookBinding::inflate) override val binding by viewBinding(ActivityArrangeBookBinding::inflate)
override val viewModel by viewModels<ArrangeBookViewModel>() override val viewModel by viewModels<BookshelfManageViewModel>()
override val groupList: ArrayList<BookGroup> = arrayListOf() override val groupList: ArrayList<BookGroup> = arrayListOf()
private val groupRequestCode = 22 private val groupRequestCode = 22
private val addToGroupRequestCode = 34 private val addToGroupRequestCode = 34
private val adapter by lazy { ArrangeBookAdapter(this, this) } private val adapter by lazy { BookAdapter(this, this) }
private var booksFlowJob: Job? = null private var booksFlowJob: Job? = null
private var menu: Menu? = null private var menu: Menu? = null
private var groupId: Long = -1 private var groupId: Long = -1
@ -70,7 +71,7 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
} }
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.arrange_book, menu) menuInflater.inflate(R.menu.bookshelf_manage, menu)
return super.onCompatCreateOptionsMenu(menu) return super.onCompatCreateOptionsMenu(menu)
} }
@ -107,7 +108,7 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
// Note: need judge selection first, so add ItemTouchHelper after it. // Note: need judge selection first, so add ItemTouchHelper after it.
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(binding.recyclerView) ItemTouchHelper(itemTouchCallback).attachToRecyclerView(binding.recyclerView)
binding.selectActionBar.setMainActionText(R.string.move_to_group) binding.selectActionBar.setMainActionText(R.string.move_to_group)
binding.selectActionBar.inflateMenu(R.menu.arrange_book_sel) binding.selectActionBar.inflateMenu(R.menu.bookshelf_menage_sel)
binding.selectActionBar.setOnMenuItemClickListener(this) binding.selectActionBar.setOnMenuItemClickListener(this)
binding.selectActionBar.setCallBack(this) binding.selectActionBar.setCallBack(this)
binding.composeView.setContent { binding.composeView.setContent {
@ -139,6 +140,7 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
booksFlowJob?.cancel() booksFlowJob?.cancel()
booksFlowJob = launch { booksFlowJob = launch {
when (groupId) { when (groupId) {
AppConst.rootGroupId -> appDb.bookDao.flowNoGroup()
AppConst.bookGroupAllId -> appDb.bookDao.flowAll() AppConst.bookGroupAllId -> appDb.bookDao.flowAll()
AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal() AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal()
AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio() AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio()
@ -146,12 +148,18 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
else -> appDb.bookDao.flowByGroup(groupId) else -> appDb.bookDao.flowByGroup(groupId)
}.conflate().map { books -> }.conflate().map { books ->
when (getPrefInt(PreferKey.bookshelfSort)) { when (getPrefInt(PreferKey.bookshelfSort)) {
1 -> books.sortedByDescending { it.latestChapterTime } 1 -> books.sortedByDescending {
it.latestChapterTime
}
2 -> books.sortedWith { o1, o2 -> 2 -> books.sortedWith { o1, o2 ->
o1.name.cnCompare(o2.name) o1.name.cnCompare(o2.name)
} }
3 -> books.sortedBy { it.order } 3 -> books.sortedBy {
else -> books.sortedByDescending { it.durChapterTime } it.order
}
else -> books.sortedByDescending {
it.durChapterTime
}
} }
}.conflate().collect { books -> }.conflate().collect { books ->
adapter.setItems(books) adapter.setItems(books)

@ -1,4 +1,4 @@
package io.legado.app.ui.book.arrange package io.legado.app.ui.book.manage
import android.app.Application import android.app.Application
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -10,7 +10,7 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
class ArrangeBookViewModel(application: Application) : BaseViewModel(application) { class BookshelfManageViewModel(application: Application) : BaseViewModel(application) {
val batchChangeSourceState = mutableStateOf(false) val batchChangeSourceState = mutableStateOf(false)
val batchChangeSourceSize = mutableStateOf(0) val batchChangeSourceSize = mutableStateOf(0)
@ -50,6 +50,7 @@ class ArrangeBookViewModel(application: Application) : BaseViewModel(application
.getOrNull()?.let { newBook -> .getOrNull()?.let { newBook ->
val toc = WebBook.getChapterListAwait(this, source, newBook) val toc = WebBook.getChapterListAwait(this, source, newBook)
book.changeTo(newBook, toc) book.changeTo(newBook, toc)
appDb.bookDao.insert(newBook)
appDb.bookChapterDao.insert(*toc.toTypedArray()) appDb.bookChapterDao.insert(*toc.toTypedArray())
} }
} }

@ -1,4 +1,4 @@
package io.legado.app.ui.book.arrange package io.legado.app.ui.book.manage
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.material.AlertDialog import androidx.compose.material.AlertDialog

@ -1,4 +1,4 @@
package io.legado.app.ui.book.arrange package io.legado.app.ui.book.manage
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle

@ -724,6 +724,7 @@ class ReadBookActivity : BaseReadBookActivity(),
ReadAloud.stop(this) ReadAloud.stop(this)
launch { launch {
ReadBook.book?.changeTo(book, toc) ReadBook.book?.changeTo(book, toc)
appDb.bookDao.insert(book)
} }
startActivity<AudioPlayActivity> { startActivity<AudioPlayActivity> {
putExtra("bookUrl", book.bookUrl) putExtra("bookUrl", book.bookUrl)

@ -195,7 +195,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
changeSourceCoroutine?.cancel() changeSourceCoroutine?.cancel()
changeSourceCoroutine = execute { changeSourceCoroutine = execute {
ReadBook.upMsg(context.getString(R.string.loading)) ReadBook.upMsg(context.getString(R.string.loading))
ReadBook.book!!.changeTo(book, toc) ReadBook.book?.changeTo(book, toc)
val nextChapter = toc.getOrElse(book.durChapterIndex) { val nextChapter = toc.getOrElse(book.durChapterIndex) {
toc.first() toc.first()
} }
@ -206,17 +206,17 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
bookChapter = toc[book.durChapterIndex], bookChapter = toc[book.durChapterIndex],
nextChapterUrl = nextChapter.url nextChapterUrl = nextChapter.url
) )
appDb.bookDao.insert(book)
appDb.bookChapterDao.insert(*toc.toTypedArray()) appDb.bookChapterDao.insert(*toc.toTypedArray())
ReadBook.resetData(book) ReadBook.resetData(book)
ReadBook.upMsg(null) ReadBook.upMsg(null)
ReadBook.loadContent(resetPageOffset = true) ReadBook.loadContent(resetPageOffset = true)
}.timeout(60000) }.onError {
.onError { context.toastOnUi("换源失败\n${it.localizedMessage}")
context.toastOnUi("换源失败\n${it.localizedMessage}") ReadBook.upMsg(null)
ReadBook.upMsg(null) }.onFinally {
}.onFinally { postEvent(EventBus.SOURCE_CHANGED, book.bookUrl)
postEvent(EventBus.SOURCE_CHANGED, book.bookUrl) }
}
} }
/** /**

@ -19,10 +19,10 @@ import io.legado.app.help.DirectLinkUpload
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.about.AppLogDialog import io.legado.app.ui.about.AppLogDialog
import io.legado.app.ui.book.arrange.ArrangeBookActivity
import io.legado.app.ui.book.cache.CacheActivity import io.legado.app.ui.book.cache.CacheActivity
import io.legado.app.ui.book.group.GroupManageDialog import io.legado.app.ui.book.group.GroupManageDialog
import io.legado.app.ui.book.local.ImportBookActivity import io.legado.app.ui.book.local.ImportBookActivity
import io.legado.app.ui.book.manage.BookshelfManageActivity
import io.legado.app.ui.book.search.SearchActivity import io.legado.app.ui.book.search.SearchActivity
import io.legado.app.ui.document.HandleFileContract import io.legado.app.ui.document.HandleFileContract
import io.legado.app.ui.main.MainViewModel import io.legado.app.ui.main.MainViewModel
@ -80,7 +80,7 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
R.id.menu_group_manage -> showDialogFragment<GroupManageDialog>() R.id.menu_group_manage -> showDialogFragment<GroupManageDialog>()
R.id.menu_add_local -> startActivity<ImportBookActivity>() R.id.menu_add_local -> startActivity<ImportBookActivity>()
R.id.menu_add_url -> addBookByUrl() R.id.menu_add_url -> addBookByUrl()
R.id.menu_arrange_bookshelf -> startActivity<ArrangeBookActivity> { R.id.menu_bookshelf_manage -> startActivity<BookshelfManageActivity> {
putExtra("groupId", groupId) putExtra("groupId", groupId)
} }
R.id.menu_download -> startActivity<CacheActivity> { R.id.menu_download -> startActivity<CacheActivity> {

@ -43,7 +43,6 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
BaseBooksAdapter.CallBack { BaseBooksAdapter.CallBack {
private val binding by viewBinding(FragmentBookshelf1Binding::bind) private val binding by viewBinding(FragmentBookshelf1Binding::bind)
private val rootGroupId = -100L
private val bookshelfLayout by lazy { private val bookshelfLayout by lazy {
getPrefInt(PreferKey.bookshelfLayout) getPrefInt(PreferKey.bookshelfLayout)
} }
@ -56,7 +55,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
private var bookGroups: List<BookGroup> = emptyList() private var bookGroups: List<BookGroup> = emptyList()
private var booksFlowJob: Job? = null private var booksFlowJob: Job? = null
override var groupId = rootGroupId override var groupId = AppConst.rootGroupId
override var books: List<Book> = emptyList() override var books: List<Book> = emptyList()
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
@ -121,7 +120,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
booksFlowJob?.cancel() booksFlowJob?.cancel()
booksFlowJob = launch { booksFlowJob = launch {
when (groupId) { when (groupId) {
rootGroupId -> appDb.bookDao.flowRoot() AppConst.rootGroupId -> appDb.bookDao.flowRoot()
AppConst.bookGroupAllId -> appDb.bookDao.flowAll() AppConst.bookGroupAllId -> appDb.bookDao.flowAll()
AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal() AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal()
AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio() AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio()
@ -212,7 +211,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return if (groupId == rootGroupId) { return if (groupId == AppConst.rootGroupId) {
bookGroups.size + books.size bookGroups.size + books.size
} else { } else {
books.size books.size
@ -220,7 +219,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
override fun getItemType(position: Int): Int { override fun getItemType(position: Int): Int {
if (groupId != rootGroupId) { if (groupId != AppConst.rootGroupId) {
return 0 return 0
} }
if (position < bookGroups.size) { if (position < bookGroups.size) {
@ -230,7 +229,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
override fun getItem(position: Int): Any? { override fun getItem(position: Int): Any? {
if (groupId != rootGroupId) { if (groupId != AppConst.rootGroupId) {
return books.getOrNull(position) return books.getOrNull(position)
} }
if (position < bookGroups.size) { if (position < bookGroups.size) {

@ -9,7 +9,7 @@
android:id="@+id/title_bar" android:id="@+id/title_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:title="@string/arrange_bookshelf" app:title="@string/bookshelf_management"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView

@ -28,6 +28,12 @@
android:title="@string/add_url" android:title="@string/add_url"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/menu_bookshelf_manage"
android:icon="@drawable/ic_arrange"
android:title="@string/bookshelf_management"
app:showAsAction="never" />
<item <item
android:id="@+id/menu_download" android:id="@+id/menu_download"
android:icon="@drawable/ic_download_line" android:icon="@drawable/ic_download_line"
@ -46,12 +52,6 @@
android:title="@string/bookshelf_layout" android:title="@string/bookshelf_layout"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/menu_arrange_bookshelf"
android:icon="@drawable/ic_arrange"
android:title="@string/arrange_bookshelf"
app:showAsAction="never" />
<item <item
android:id="@+id/menu_export_bookshelf" android:id="@+id/menu_export_bookshelf"
android:icon="@drawable/ic_export" android:icon="@drawable/ic_export"

@ -267,7 +267,7 @@
<string name="load_over_time">Tiempo límite de carga</string> <string name="load_over_time">Tiempo límite de carga</string>
<string name="join_group">Siga:%s</string> <string name="join_group">Siga:%s</string>
<string name="copy_complete">Copiado con éxito</string> <string name="copy_complete">Copiado con éxito</string>
<string name="arrange_bookshelf">Organización de estantería</string> <string name="bookshelf_management">Organización de estantería</string>
<string name="clear_bookshelf_s">Esto borrará a todos los libros. Por favor, tenga cuidado.</string> <string name="clear_bookshelf_s">Esto borrará a todos los libros. Por favor, tenga cuidado.</string>
<string name="search_book_source">Buscar fuentes de libros</string> <string name="search_book_source">Buscar fuentes de libros</string>
<string name="search_rss_source">Buscar fuentes de suscripción</string> <string name="search_rss_source">Buscar fuentes de suscripción</string>

@ -271,7 +271,7 @@
<string name="load_over_time">Loading timeout</string> <string name="load_over_time">Loading timeout</string>
<string name="join_group">Follow:%s</string> <string name="join_group">Follow:%s</string>
<string name="copy_complete">Copied successfully</string> <string name="copy_complete">Copied successfully</string>
<string name="arrange_bookshelf">Bookshelf arrangement</string> <string name="bookshelf_management">Bookshelf management</string>
<string name="clear_bookshelf_s">It will delete all books. Be careful,please.</string> <string name="clear_bookshelf_s">It will delete all books. Be careful,please.</string>
<string name="search_book_source">Search book sources</string> <string name="search_book_source">Search book sources</string>
<string name="search_rss_source">Search subscription sources</string> <string name="search_rss_source">Search subscription sources</string>

@ -271,7 +271,7 @@
<string name="load_over_time">Tempo limite de carregamento</string> <string name="load_over_time">Tempo limite de carregamento</string>
<string name="join_group">Siga:%s</string> <string name="join_group">Siga:%s</string>
<string name="copy_complete">Copiado com sucesso</string> <string name="copy_complete">Copiado com sucesso</string>
<string name="arrange_bookshelf">A organização da estante</string> <string name="bookshelf_management">A organização da estante</string>
<string name="clear_bookshelf_s">Isto excluirá todos os livros. Por favor, tenha cuidado.</string> <string name="clear_bookshelf_s">Isto excluirá todos os livros. Por favor, tenha cuidado.</string>
<string name="search_book_source">Pesquisar fontes de livros</string> <string name="search_book_source">Pesquisar fontes de livros</string>
<string name="search_rss_source">Buscar fontes de assinatura</string> <string name="search_rss_source">Buscar fontes de assinatura</string>

@ -268,7 +268,7 @@
<string name="load_over_time">加載超時</string> <string name="load_over_time">加載超時</string>
<string name="join_group">關注: %s</string> <string name="join_group">關注: %s</string>
<string name="copy_complete">拷貝咗</string> <string name="copy_complete">拷貝咗</string>
<string name="arrange_bookshelf">整理書架</string> <string name="bookshelf_management">書架管理</string>
<string name="clear_bookshelf_s">這將會刪除所有書籍,請謹慎操作。</string> <string name="clear_bookshelf_s">這將會刪除所有書籍,請謹慎操作。</string>
<string name="search_book_source">搜索書源</string> <string name="search_book_source">搜索書源</string>
<string name="search_rss_source">搜索訂閲源</string> <string name="search_rss_source">搜索訂閲源</string>

@ -270,7 +270,7 @@
<string name="load_over_time">載入超時</string> <string name="load_over_time">載入超時</string>
<string name="join_group">關注:%s</string> <string name="join_group">關注:%s</string>
<string name="copy_complete">已複製</string> <string name="copy_complete">已複製</string>
<string name="arrange_bookshelf">整理書架</string> <string name="bookshelf_management">書架管理</string>
<string name="clear_bookshelf_s">這將會刪除所有書籍,請謹慎操作。</string> <string name="clear_bookshelf_s">這將會刪除所有書籍,請謹慎操作。</string>
<string name="search_book_source">搜尋書源</string> <string name="search_book_source">搜尋書源</string>
<string name="search_rss_source">搜尋訂閱源</string> <string name="search_rss_source">搜尋訂閱源</string>

@ -270,7 +270,7 @@
<string name="load_over_time">加载超时</string> <string name="load_over_time">加载超时</string>
<string name="join_group">关注:%s</string> <string name="join_group">关注:%s</string>
<string name="copy_complete">已拷贝</string> <string name="copy_complete">已拷贝</string>
<string name="arrange_bookshelf">整理书架</string> <string name="bookshelf_management">书架管理</string>
<string name="clear_bookshelf_s">这将会删除所有书籍,请谨慎操作。</string> <string name="clear_bookshelf_s">这将会删除所有书籍,请谨慎操作。</string>
<string name="search_book_source">搜索书源</string> <string name="search_book_source">搜索书源</string>
<string name="search_rss_source">搜索订阅源</string> <string name="search_rss_source">搜索订阅源</string>

@ -271,7 +271,7 @@
<string name="load_over_time">Loading timeout</string> <string name="load_over_time">Loading timeout</string>
<string name="join_group">Follow:%s</string> <string name="join_group">Follow:%s</string>
<string name="copy_complete">Copied successfully</string> <string name="copy_complete">Copied successfully</string>
<string name="arrange_bookshelf">Bookshelf arrangement</string> <string name="bookshelf_management">Bookshelf management</string>
<string name="clear_bookshelf_s">It will delete all books. Be careful,please.</string> <string name="clear_bookshelf_s">It will delete all books. Be careful,please.</string>
<string name="search_book_source">Search book sources</string> <string name="search_book_source">Search book sources</string>
<string name="search_rss_source">Search subscription sources</string> <string name="search_rss_source">Search subscription sources</string>

Loading…
Cancel
Save