mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
优化
This commit is contained in:
@@ -108,6 +108,7 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
|
||||
@Synchronized
|
||||
fun setItems(items: List<ITEM>?, itemCallback: DiffUtil.ItemCallback<ITEM>) {
|
||||
kotlin.runCatching {
|
||||
val oldItems = this.items.toList()
|
||||
val callback = object : DiffUtil.Callback() {
|
||||
override fun getOldListSize(): Int {
|
||||
return itemCount
|
||||
@@ -118,7 +119,7 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = getItem(oldItemPosition - getHeaderCount())
|
||||
val oldItem = oldItems.getOrNull(oldItemPosition - getHeaderCount())
|
||||
?: return true
|
||||
val newItem = items?.getOrNull(newItemPosition - getHeaderCount())
|
||||
?: return true
|
||||
@@ -129,7 +130,7 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
|
||||
oldItemPosition: Int,
|
||||
newItemPosition: Int
|
||||
): Boolean {
|
||||
val oldItem = getItem(oldItemPosition - getHeaderCount())
|
||||
val oldItem = oldItems.getOrNull(oldItemPosition - getHeaderCount())
|
||||
?: return true
|
||||
val newItem = items?.getOrNull(newItemPosition - getHeaderCount())
|
||||
?: return true
|
||||
@@ -137,7 +138,7 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
|
||||
val oldItem = getItem(oldItemPosition - getHeaderCount())
|
||||
val oldItem = oldItems.getOrNull(oldItemPosition - getHeaderCount())
|
||||
?: return null
|
||||
val newItem = items?.getOrNull(newItemPosition - getHeaderCount())
|
||||
?: return null
|
||||
|
||||
@@ -44,7 +44,7 @@ object BookType {
|
||||
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@IntDef(text, updateError, audio, image, webFile, local)
|
||||
@IntDef(text, updateError, audio, image, webFile, local, archive)
|
||||
annotation class Type
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ interface TxtTocRuleDao {
|
||||
@get:Query("select * from txtTocRules where enable != 1 order by serialNumber")
|
||||
val disabled: List<TxtTocRule>
|
||||
|
||||
@get:Query("select count(*) from txtTocRules")
|
||||
val count: Int
|
||||
|
||||
@Query("select * from txtTocRules where id = :id")
|
||||
fun get(id: Long): TxtTocRule?
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.legado.app.R
|
||||
import io.legado.app.constant.IntentAction
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.help.CacheManager
|
||||
import io.legado.app.help.IntentData
|
||||
import io.legado.app.service.CheckSourceService
|
||||
import io.legado.app.utils.startService
|
||||
import splitties.init.appCtx
|
||||
@@ -26,9 +27,9 @@ object CheckSource {
|
||||
sources.map {
|
||||
selectedIds.add(it.bookSourceUrl)
|
||||
}
|
||||
IntentData.put("checkSourceSelectedIds", selectedIds)
|
||||
context.startService<CheckSourceService> {
|
||||
action = IntentAction.start
|
||||
putExtra("selectIds", selectedIds)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -212,11 +212,8 @@ object LocalBook {
|
||||
val files = ArchiveUtils.deCompress(archiveFileDoc, filter = filter)
|
||||
if (files.isEmpty()) throw NoStackTraceException(appCtx.getString(R.string.unsupport_archivefile_entry))
|
||||
return files.map {
|
||||
saveBookFile(
|
||||
FileInputStream(it),
|
||||
saveFileName ?: it.name
|
||||
).let {
|
||||
importFile(it).apply {
|
||||
saveBookFile(FileInputStream(it), saveFileName ?: it.name).let { uri ->
|
||||
importFile(uri).apply {
|
||||
//附加压缩包名称 以便解压文件被删后再解压
|
||||
origin = "${BookType.localTag}::${archiveFileDoc.name}"
|
||||
addType(BookType.archive)
|
||||
@@ -255,8 +252,8 @@ object LocalBook {
|
||||
importFile(uri)
|
||||
}
|
||||
}.onFailure {
|
||||
AppLog.put("ImportFile Error:\nFile ${fileDoc.toString()}\n${it.localizedMessage}", it)
|
||||
errorCount = errorCount + 1
|
||||
AppLog.put("ImportFile Error:\nFile $fileDoc\n${it.localizedMessage}", it)
|
||||
errorCount += 1
|
||||
}
|
||||
}
|
||||
if (errorCount == uris.size) throw NoStackTraceException("ImportFiles Error:\nAll input files occur error")
|
||||
@@ -409,8 +406,8 @@ object LocalBook {
|
||||
if (localBook.isArchive) {
|
||||
// 压缩包
|
||||
val archiveUri = saveBookFile(it, localBook.archiveName)
|
||||
val newBook = importArchiveFile(archiveUri, localBook.originName) {
|
||||
it.contains(localBook.originName)
|
||||
val newBook = importArchiveFile(archiveUri, localBook.originName) { name ->
|
||||
name.contains(localBook.originName)
|
||||
}.first()
|
||||
localBook.origin = newBook.origin
|
||||
localBook.bookUrl = newBook.bookUrl
|
||||
|
||||
@@ -407,7 +407,7 @@ class TextFile(private val book: Book) {
|
||||
*/
|
||||
private fun getTocRules(): List<TxtTocRule> {
|
||||
var rules = appDb.txtTocRuleDao.enabled
|
||||
if (rules.isEmpty()) {
|
||||
if (appDb.txtTocRuleDao.count == 0) {
|
||||
rules = DefaultData.txtTocRules.apply {
|
||||
appDb.txtTocRuleDao.insert(*this.toTypedArray())
|
||||
}.filter {
|
||||
|
||||
@@ -78,6 +78,7 @@ abstract class BaseReadAloudService : BaseService(),
|
||||
internal var pageIndex = 0
|
||||
private var needResumeOnAudioFocusGain = false
|
||||
private var dsJob: Job? = null
|
||||
var pageChanged = false
|
||||
|
||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
@@ -157,7 +158,7 @@ abstract class BaseReadAloudService : BaseService(),
|
||||
contentList.add(text)
|
||||
}
|
||||
}
|
||||
if (play) play()
|
||||
if (play) play() else pageChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.exception.ContentEmptyException
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.exception.TocEmptyException
|
||||
import io.legado.app.help.IntentData
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.source.exploreKinds
|
||||
import io.legado.app.model.CheckSource
|
||||
@@ -65,12 +66,12 @@ class CheckSourceService : BaseService() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
when (intent?.action) {
|
||||
IntentAction.start -> intent.getStringArrayListExtra("selectIds")?.let {
|
||||
IntentAction.start -> IntentData.get<ArrayList<String>>("checkSourceSelectedIds")?.let {
|
||||
check(it)
|
||||
}
|
||||
|
||||
IntentAction.resume -> upNotification()
|
||||
else -> stopSelf()
|
||||
IntentAction.stop -> stopSelf()
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
||||
}
|
||||
|
||||
override fun play() {
|
||||
pageChanged = false
|
||||
exoPlayer.stop()
|
||||
if (contentList.isEmpty()) {
|
||||
AppLog.putDebug("朗读列表为空")
|
||||
@@ -285,8 +286,12 @@ class HttpReadAloudService : BaseReadAloudService(),
|
||||
override fun resumeReadAloud() {
|
||||
super.resumeReadAloud()
|
||||
kotlin.runCatching {
|
||||
exoPlayer.play()
|
||||
upPlayPos()
|
||||
if (pageChanged) {
|
||||
play()
|
||||
} else {
|
||||
exoPlayer.play()
|
||||
upPlayPos()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ abstract class BaseImportBookActivity<VM : ViewModel> : VMBaseActivity<ActivityI
|
||||
binding.titleBar.findViewById(R.id.search_view)
|
||||
}
|
||||
|
||||
private val localBookTreeSelect = registerForActivityResult(HandleFileContract()) {
|
||||
val localBookTreeSelect = registerForActivityResult(HandleFileContract()) {
|
||||
it.uri?.let { treeUri ->
|
||||
AppConfig.defaultBookTreeUri = treeUri.toString()
|
||||
localBookTreeSelectListener?.invoke(true)
|
||||
@@ -61,10 +61,12 @@ abstract class BaseImportBookActivity<VM : ViewModel> : VMBaseActivity<ActivityI
|
||||
*/
|
||||
protected suspend fun setBookStorage() = suspendCoroutine { block ->
|
||||
localBookTreeSelectListener = {
|
||||
localBookTreeSelectListener = null
|
||||
block.resume(it)
|
||||
}
|
||||
//测试书籍保存位置是否设置
|
||||
if (!AppConfig.defaultBookTreeUri.isNullOrBlank()) {
|
||||
localBookTreeSelectListener = null
|
||||
block.resume(true)
|
||||
return@suspendCoroutine
|
||||
}
|
||||
@@ -78,9 +80,11 @@ abstract class BaseImportBookActivity<VM : ViewModel> : VMBaseActivity<ActivityI
|
||||
}
|
||||
}
|
||||
noButton {
|
||||
localBookTreeSelectListener = null
|
||||
block.resume(false)
|
||||
}
|
||||
onCancelled {
|
||||
localBookTreeSelectListener = null
|
||||
block.resume(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,14 @@ class RemoteBookActivity : BaseImportBookActivity<RemoteBookViewModel>(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun observeLiveBus() {
|
||||
viewModel.permissionDenialLiveData.observe(this) {
|
||||
localBookTreeSelect.launch {
|
||||
title = getString(R.string.select_book_folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.layTop.setBackgroundColor(backgroundColor)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.legado.app.ui.book.import.remote
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.constant.AppLog
|
||||
import io.legado.app.constant.BookType
|
||||
@@ -25,6 +26,7 @@ class RemoteBookViewModel(application: Application) : BaseViewModel(application)
|
||||
var sortKey = RemoteBookSort.Default
|
||||
var sortAscending = false
|
||||
val dirList = arrayListOf<RemoteBook>()
|
||||
val permissionDenialLiveData = MutableLiveData<Int>()
|
||||
|
||||
var dataCallback: DataCallback? = null
|
||||
|
||||
@@ -141,6 +143,9 @@ class RemoteBookViewModel(application: Application) : BaseViewModel(application)
|
||||
}.onError {
|
||||
AppLog.put("导入出错\n${it.localizedMessage}", it)
|
||||
context.toastOnUi("导入出错\n${it.localizedMessage}")
|
||||
if (it is SecurityException) {
|
||||
permissionDenialLiveData.postValue(1)
|
||||
}
|
||||
}.onFinally {
|
||||
finally.invoke()
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
||||
ItemTouchCallback.Callback {
|
||||
|
||||
private val selected = linkedSetOf<BookSource>()
|
||||
private val finalMessageRegex = Regex("成功|失败")
|
||||
|
||||
val selection: List<BookSource>
|
||||
get() {
|
||||
@@ -94,9 +95,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
||||
cbBookSource.text = item.getDisPlayNameGroup()
|
||||
swtEnabled.isChecked = item.enabled
|
||||
cbBookSource.isChecked = selected.contains(item)
|
||||
ivDebugText.text = Debug.debugMessageMap[item.bookSourceUrl] ?: ""
|
||||
ivDebugText.visibility =
|
||||
if (ivDebugText.text.toString().isNotBlank()) View.VISIBLE else View.GONE
|
||||
upCheckSourceMessage(binding, item)
|
||||
upShowExplore(ivExplore, item)
|
||||
} else {
|
||||
payload.keySet().map {
|
||||
@@ -105,21 +104,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
||||
"upName" -> cbBookSource.text = item.getDisPlayNameGroup()
|
||||
"upExplore" -> upShowExplore(ivExplore, item)
|
||||
"selected" -> cbBookSource.isChecked = selected.contains(item)
|
||||
"checkSourceMessage" -> {
|
||||
ivDebugText.text = Debug.debugMessageMap[item.bookSourceUrl] ?: ""
|
||||
val isEmpty = ivDebugText.text.toString().isEmpty()
|
||||
var isFinalMessage =
|
||||
ivDebugText.text.toString().contains(Regex("成功|失败"))
|
||||
if (!Debug.isChecking && !isFinalMessage) {
|
||||
Debug.updateFinalMessage(item.bookSourceUrl, "校验失败")
|
||||
ivDebugText.text = Debug.debugMessageMap[item.bookSourceUrl] ?: ""
|
||||
isFinalMessage = true
|
||||
}
|
||||
ivDebugText.visibility =
|
||||
if (!isEmpty) View.VISIBLE else View.GONE
|
||||
ivProgressBar.visibility =
|
||||
if (isFinalMessage || isEmpty || !Debug.isChecking) View.GONE else View.VISIBLE
|
||||
}
|
||||
"checkSourceMessage" -> upCheckSourceMessage(binding, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,6 +205,22 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
|
||||
}
|
||||
}
|
||||
|
||||
private fun upCheckSourceMessage(binding: ItemBookSourceBinding, item: BookSource) = binding.run {
|
||||
val msg = Debug.debugMessageMap[item.bookSourceUrl] ?: ""
|
||||
ivDebugText.text = msg
|
||||
val isEmpty = msg.isEmpty()
|
||||
var isFinalMessage = msg.contains(finalMessageRegex)
|
||||
if (!Debug.isChecking && !isFinalMessage) {
|
||||
Debug.updateFinalMessage(item.bookSourceUrl, "校验失败")
|
||||
ivDebugText.text = Debug.debugMessageMap[item.bookSourceUrl] ?: ""
|
||||
isFinalMessage = true
|
||||
}
|
||||
ivDebugText.visibility =
|
||||
if (!isEmpty) View.VISIBLE else View.GONE
|
||||
ivProgressBar.visibility =
|
||||
if (isFinalMessage || isEmpty || !Debug.isChecking) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
fun selectAll() {
|
||||
getItems().forEach {
|
||||
selected.add(it)
|
||||
|
||||
@@ -7,7 +7,9 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.legado.app.R
|
||||
@@ -30,7 +32,6 @@ import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -108,7 +109,7 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
launch {
|
||||
appDb.txtTocRuleDao.observeAll().conflate().collect { tocRules ->
|
||||
initSelectedName(tocRules)
|
||||
adapter.setItems(tocRules)
|
||||
adapter.setItems(tocRules, adapter.diffItemCallBack)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +192,43 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
RecyclerAdapter<TxtTocRule, ItemTocRegexBinding>(context),
|
||||
ItemTouchCallback.Callback {
|
||||
|
||||
val diffItemCallBack = object : DiffUtil.ItemCallback<TxtTocRule>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: TxtTocRule, newItem: TxtTocRule): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: TxtTocRule, newItem: TxtTocRule): Boolean {
|
||||
if (oldItem.name != newItem.name) {
|
||||
return false
|
||||
}
|
||||
if (oldItem.enable != newItem.enable) {
|
||||
return false
|
||||
}
|
||||
if (oldItem.example != newItem.example) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItem: TxtTocRule, newItem: TxtTocRule): Any? {
|
||||
val payload = Bundle()
|
||||
if (oldItem.name != newItem.name) {
|
||||
payload.putBoolean("upName", true)
|
||||
}
|
||||
if (oldItem.enable != newItem.enable) {
|
||||
payload.putBoolean("enabled", newItem.enable)
|
||||
}
|
||||
if (oldItem.example != newItem.example) {
|
||||
payload.putBoolean("upExample", true)
|
||||
}
|
||||
if (payload.isEmpty) {
|
||||
return null
|
||||
}
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemTocRegexBinding {
|
||||
return ItemTocRegexBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
@@ -202,14 +240,22 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
binding.apply {
|
||||
if (payloads.isEmpty()) {
|
||||
val bundle = payloads.getOrNull(0) as? Bundle
|
||||
if (bundle == null) {
|
||||
root.setBackgroundColor(context.backgroundColor)
|
||||
rbRegexName.text = item.name
|
||||
titleExample.text = item.example
|
||||
rbRegexName.isChecked = item.name == selectedName
|
||||
swtEnabled.isChecked = item.enable
|
||||
} else {
|
||||
rbRegexName.isChecked = item.name == selectedName
|
||||
bundle.keySet().map {
|
||||
when (it) {
|
||||
"upNmae" -> rbRegexName.text = item.name
|
||||
"upExample" -> titleExample.text = item.example
|
||||
"enabled" -> swtEnabled.isChecked = item.enable
|
||||
"upSelect" -> rbRegexName.isChecked = item.name == selectedName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,16 +265,14 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
rbRegexName.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
if (buttonView.isPressed && isChecked) {
|
||||
selectedName = getItem(holder.layoutPosition)?.name
|
||||
updateItems(0, itemCount - 1, true)
|
||||
updateItems(0, itemCount - 1, bundleOf("upSelect" to null))
|
||||
}
|
||||
}
|
||||
swtEnabled.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
if (buttonView.isPressed) {
|
||||
getItem(holder.layoutPosition)?.let {
|
||||
it.enable = isChecked
|
||||
launch(IO) {
|
||||
appDb.txtTocRuleDao.update(it)
|
||||
}
|
||||
viewModel.update(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,9 +281,7 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
}
|
||||
ivDelete.setOnClickListener {
|
||||
getItem(holder.layoutPosition)?.let { item ->
|
||||
launch(IO) {
|
||||
appDb.txtTocRuleDao.delete(item)
|
||||
}
|
||||
viewModel.del(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,9 +301,7 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
for ((index, item) in getItems().withIndex()) {
|
||||
item.serialNumber = index + 1
|
||||
}
|
||||
launch(IO) {
|
||||
appDb.txtTocRuleDao.update(*getItems().toTypedArray())
|
||||
}
|
||||
viewModel.update(*getItems().toTypedArray())
|
||||
}
|
||||
isMoved = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user