pull/737/head
Robot 4 years ago
commit ba410116b1
  1. 1
      app/src/main/assets/updateLog.md
  2. 165
      app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt
  3. 31
      app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt
  4. 264
      app/src/main/java/io/legado/app/base/adapter/SimpleRecyclerAdapter.kt
  5. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  6. 2
      app/src/main/java/io/legado/app/help/AppConfig.kt
  7. 1
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  8. 4
      app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt
  9. 4
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowAdapter.kt
  10. 19
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  11. 4
      app/src/main/java/io/legado/app/ui/book/read/config/BgAdapter.kt
  12. 4
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  13. 2
      app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt
  14. 2
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt
  15. 2
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  16. 2
      app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt
  17. 2
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
  18. 2
      app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
  19. 4
      app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt
  20. 2
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt
  21. 4
      app/src/main/java/io/legado/app/ui/rss/article/BaseRssArticlesAdapter.kt
  22. 2
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  23. 2
      app/src/main/res/values-zh-rHK/strings.xml
  24. 2
      app/src/main/res/values-zh-rTW/strings.xml
  25. 2
      app/src/main/res/values-zh/strings.xml
  26. 2
      app/src/main/res/values/strings.xml
  27. 9
      app/src/main/res/xml/pref_config_backup.xml

@ -6,6 +6,7 @@
**2020/12/15** **2020/12/15**
* 修复一些引起崩溃的bug * 修复一些引起崩溃的bug
* 修复搜书和换源可能什么分组都没有的bug * 修复搜书和换源可能什么分组都没有的bug
* 添加同步进度开关,默认开启
**2020/12/14** **2020/12/14**
* 修复bug * 修复bug

@ -4,13 +4,11 @@ import android.content.Context
import android.util.SparseArray import android.util.SparseArray
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import java.util.* import java.util.*
import kotlin.collections.ArrayList
/** /**
* Created by Invincible on 2017/11/24. * Created by Invincible on 2017/11/24.
@ -21,27 +19,12 @@ import kotlin.collections.ArrayList
abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Context) : abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Context) :
RecyclerView.Adapter<ItemViewHolder>() { RecyclerView.Adapter<ItemViewHolder>() {
constructor(context: Context, vararg delegates: ItemViewDelegate<ITEM, VB>) : this(context) {
addItemViewDelegates(*delegates)
}
constructor(
context: Context,
vararg delegates: Pair<Int, ItemViewDelegate<ITEM, VB>>
) : this(context) {
addItemViewDelegates(*delegates)
}
val inflater: LayoutInflater = LayoutInflater.from(context) val inflater: LayoutInflater = LayoutInflater.from(context)
private val headerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } private val headerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() }
private val footerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } private val footerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() }
private val itemDelegates: HashMap<Int, ItemViewDelegate<ITEM, VB>> = hashMapOf() private val items: MutableList<ITEM> = mutableListOf()
private val asyncListDiffer: AsyncListDiffer<ITEM> by lazy {
AsyncListDiffer(this, diffItemCallback)
}
private val lock = Object() private val lock = Object()
@ -50,19 +33,6 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
var itemAnimation: ItemAnimation? = null var itemAnimation: ItemAnimation? = null
open val diffItemCallback: DiffUtil.ItemCallback<ITEM> =
object : DiffUtil.ItemCallback<ITEM>() {
override fun areItemsTheSame(oldItem: ITEM, newItem: ITEM): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: ITEM, newItem: ITEM): Boolean {
return true
}
}
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) {
itemClickListener = listener itemClickListener = listener
} }
@ -75,28 +45,6 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
recyclerView.adapter = this recyclerView.adapter = this
} }
fun <DELEGATE : ItemViewDelegate<ITEM, VB>> addItemViewDelegate(
viewType: Int,
delegate: DELEGATE
) {
itemDelegates[viewType] = delegate
}
fun addItemViewDelegate(delegate: ItemViewDelegate<ITEM, VB>) {
itemDelegates[itemDelegates.size] = delegate
}
fun addItemViewDelegates(vararg delegates: ItemViewDelegate<ITEM, VB>) {
delegates.forEach {
addItemViewDelegate(it)
}
}
fun addItemViewDelegates(vararg delegates: Pair<Int, ItemViewDelegate<ITEM, VB>>) =
delegates.forEach {
addItemViewDelegate(it.first, it.second)
}
fun addHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) { fun addHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) {
synchronized(lock) { synchronized(lock) {
val index = headerItems.size() val index = headerItems.size()
@ -133,65 +81,88 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun setItems(items: List<ITEM>?) { fun setItems(items: List<ITEM>?) {
synchronized(lock) { synchronized(lock) {
asyncListDiffer.submitList(items) if (this.items.isNotEmpty()) {
this.items.clear()
}
if (items != null) {
this.items.addAll(items)
}
notifyDataSetChanged()
}
}
fun setItems(items: List<ITEM>?, diffResult: DiffUtil.DiffResult) {
synchronized(lock) {
if (this.items.isNotEmpty()) {
this.items.clear()
}
if (items != null) {
this.items.addAll(items)
}
diffResult.dispatchUpdatesTo(this)
} }
} }
fun setItem(position: Int, item: ITEM) { fun setItem(position: Int, item: ITEM) {
synchronized(lock) { synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList) val oldSize = getActualItemCount()
list[position] = item if (position in 0 until oldSize) {
asyncListDiffer.submitList(list) this.items[position] = item
notifyItemChanged(position + getHeaderCount())
}
} }
} }
fun addItem(item: ITEM) { fun addItem(item: ITEM) {
synchronized(lock) { synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList) val oldSize = getActualItemCount()
list.add(item) if (this.items.add(item)) {
asyncListDiffer.submitList(list) notifyItemInserted(oldSize + getHeaderCount())
}
} }
} }
fun addItems(position: Int, newItems: List<ITEM>) { fun addItems(position: Int, newItems: List<ITEM>) {
synchronized(lock) { synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList) if (this.items.addAll(position, newItems)) {
list.addAll(position, newItems) notifyItemRangeInserted(position + getHeaderCount(), newItems.size)
asyncListDiffer.submitList(list) }
} }
} }
fun addItems(newItems: List<ITEM>) { fun addItems(newItems: List<ITEM>) {
synchronized(lock) { synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList) val oldSize = getActualItemCount()
list.addAll(newItems) if (this.items.addAll(newItems)) {
asyncListDiffer.submitList(list) if (oldSize == 0 && getHeaderCount() == 0) {
notifyDataSetChanged()
} else {
notifyItemRangeInserted(oldSize + getHeaderCount(), newItems.size)
}
}
} }
} }
fun removeItem(position: Int) { fun removeItem(position: Int) {
synchronized(lock) { synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList) if (this.items.removeAt(position) != null) {
if (list.removeAt(position) != null) { notifyItemRemoved(position + getHeaderCount())
asyncListDiffer.submitList(list)
} }
} }
} }
fun removeItem(item: ITEM) { fun removeItem(item: ITEM) {
synchronized(lock) { synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList) if (this.items.remove(item)) {
if (list.remove(item)) { notifyItemRemoved(this.items.indexOf(item) + getHeaderCount())
asyncListDiffer.submitList(list)
} }
} }
} }
fun removeItems(items: List<ITEM>) { fun removeItems(items: List<ITEM>) {
synchronized(lock) { synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList) if (this.items.removeAll(items)) {
if (list.removeAll(items)) { notifyDataSetChanged()
asyncListDiffer.submitList(list)
} }
} }
} }
@ -202,7 +173,7 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
if (oldPosition in 0 until size && newPosition in 0 until size) { if (oldPosition in 0 until size && newPosition in 0 until size) {
val srcPosition = oldPosition + getHeaderCount() val srcPosition = oldPosition + getHeaderCount()
val targetPosition = newPosition + getHeaderCount() val targetPosition = newPosition + getHeaderCount()
Collections.swap(asyncListDiffer.currentList, srcPosition, targetPosition) Collections.swap(this.items, srcPosition, targetPosition)
notifyItemChanged(srcPosition) notifyItemChanged(srcPosition)
notifyItemChanged(targetPosition) notifyItemChanged(targetPosition)
} }
@ -211,9 +182,9 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun updateItem(item: ITEM) = fun updateItem(item: ITEM) =
synchronized(lock) { synchronized(lock) {
val index = asyncListDiffer.currentList.indexOf(item) val index = this.items.indexOf(item)
if (index >= 0) { if (index >= 0) {
asyncListDiffer.currentList[index] = item this.items[index] = item
notifyItemChanged(index) notifyItemChanged(index)
} }
} }
@ -240,17 +211,18 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun clearItems() = fun clearItems() =
synchronized(lock) { synchronized(lock) {
asyncListDiffer.submitList(arrayListOf()) this.items.clear()
notifyDataSetChanged()
} }
fun isEmpty() = asyncListDiffer.currentList.isEmpty() fun isEmpty() = items.isEmpty()
fun isNotEmpty() = asyncListDiffer.currentList.isNotEmpty() fun isNotEmpty() = items.isNotEmpty()
/** /**
* 除去header和footer * 除去header和footer
*/ */
fun getActualItemCount() = asyncListDiffer.currentList.size fun getActualItemCount() = items.size
fun getHeaderCount() = headerItems.size() fun getHeaderCount() = headerItems.size()
@ -258,12 +230,11 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun getFooterCount() = footerItems.size() fun getFooterCount() = footerItems.size()
fun getItem(position: Int): ITEM? = asyncListDiffer.currentList.getOrNull(position) fun getItem(position: Int): ITEM? = items.getOrNull(position)
fun getItemByLayoutPosition(position: Int) = fun getItemByLayoutPosition(position: Int) = items.getOrNull(position - getHeaderCount())
asyncListDiffer.currentList.getOrNull(position - getHeaderCount())
fun getItems(): List<ITEM> = asyncListDiffer.currentList fun getItems(): List<ITEM> = items
protected open fun getItemViewType(item: ITEM, position: Int) = 0 protected open fun getItemViewType(item: ITEM, position: Int) = 0
@ -295,8 +266,7 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
val holder = ItemViewHolder(getViewBinding(parent)) val holder = ItemViewHolder(getViewBinding(parent))
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
itemDelegates.getValue(viewType) registerListener(holder, (holder.binding as VB))
.registerListener(holder, (holder.binding as VB))
if (itemClickListener != null) { if (itemClickListener != null) {
holder.itemView.setOnClickListener { holder.itemView.setOnClickListener {
@ -330,8 +300,7 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
) { ) {
if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) { if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) {
getItem(holder.layoutPosition - getHeaderCount())?.let { getItem(holder.layoutPosition - getHeaderCount())?.let {
itemDelegates.getValue(getItemViewType(holder.layoutPosition)) convert(holder, (holder.binding as VB), it, payloads)
.convert(holder, (holder.binding as VB), it, payloads)
} }
} }
} }
@ -381,6 +350,22 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
} }
} }
/**
* 如果使用了事件回调,回调里不要直接使用item,会出现不更新的问题,
* 使用getItem(holder.layoutPosition)来获取item
*/
abstract fun convert(
holder: ItemViewHolder,
binding: VB,
item: ITEM,
payloads: MutableList<Any>
)
/**
* 注册事件
*/
abstract fun registerListener(holder: ItemViewHolder, binding: VB)
companion object { companion object {
private const val TYPE_HEADER_VIEW = Int.MIN_VALUE private const val TYPE_HEADER_VIEW = Int.MIN_VALUE
private const val TYPE_FOOTER_VIEW = Int.MAX_VALUE - 999 private const val TYPE_FOOTER_VIEW = Int.MAX_VALUE - 999

@ -1,31 +0,0 @@
package io.legado.app.base.adapter
import android.content.Context
import androidx.viewbinding.ViewBinding
/**
* Created by Invincible on 2017/11/24.
*
* item代理
*/
abstract class ItemViewDelegate<ITEM, VB : ViewBinding>(protected val context: Context) {
/**
* 如果使用了事件回调,回调里不要直接使用item,会出现不更新的问题,
* 使用getItem(holder.layoutPosition)来获取item,
* 或者使用registerListener(holder: ItemViewHolder, position: Int)
*/
abstract fun convert(
holder: ItemViewHolder,
binding: VB,
item: ITEM,
payloads: MutableList<Any>
)
/**
* 注册事件
*/
abstract fun registerListener(holder: ItemViewHolder, binding: VB)
}

@ -1,30 +1,265 @@
package io.legado.app.base.adapter package io.legado.app.base.adapter
import android.content.Context import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import java.util.*
import kotlin.collections.ArrayList
/** /**
* Created by Invincible on 2017/12/15. * Created by Invincible on 2017/12/15.
*/ */
abstract class SimpleRecyclerAdapter<ITEM, VB : ViewBinding>(context: Context) : @Suppress("unused")
CommonRecyclerAdapter<ITEM, VB>(context) { abstract class SimpleRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Context) :
RecyclerView.Adapter<ItemViewHolder>() {
init { val inflater: LayoutInflater = LayoutInflater.from(context)
addItemViewDelegate(object : ItemViewDelegate<ITEM, VB>(context) {
override fun convert( private val asyncListDiffer: AsyncListDiffer<ITEM> by lazy {
holder: ItemViewHolder, AsyncListDiffer(this, diffItemCallback)
binding: VB, }
item: ITEM,
payloads: MutableList<Any> private val lock = Object()
) {
this@SimpleRecyclerAdapter.convert(holder, binding, item, payloads) private var itemClickListener: ((holder: ItemViewHolder, item: ITEM) -> Unit)? = null
private var itemLongClickListener: ((holder: ItemViewHolder, item: ITEM) -> Boolean)? = null
var itemAnimation: ItemAnimation? = null
open val diffItemCallback: DiffUtil.ItemCallback<ITEM> =
object : DiffUtil.ItemCallback<ITEM>() {
override fun areItemsTheSame(oldItem: ITEM, newItem: ITEM): Boolean {
return oldItem == newItem
} }
override fun registerListener(holder: ItemViewHolder, binding: VB) { override fun areContentsTheSame(oldItem: ITEM, newItem: ITEM): Boolean {
this@SimpleRecyclerAdapter.registerListener(holder, binding) return true
} }
})
}
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) {
itemClickListener = listener
}
fun setOnItemLongClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Boolean) {
itemLongClickListener = listener
}
fun bindToRecyclerView(recyclerView: RecyclerView) {
recyclerView.adapter = this
}
fun setItems(items: List<ITEM>?) {
synchronized(lock) {
asyncListDiffer.submitList(items)
}
}
fun setItem(position: Int, item: ITEM) {
synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList)
list[position] = item
asyncListDiffer.submitList(list)
}
}
fun addItem(item: ITEM) {
synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList)
list.add(item)
asyncListDiffer.submitList(list)
}
}
fun addItems(position: Int, newItems: List<ITEM>) {
synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList)
list.addAll(position, newItems)
asyncListDiffer.submitList(list)
}
}
fun addItems(newItems: List<ITEM>) {
synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList)
list.addAll(newItems)
asyncListDiffer.submitList(list)
}
}
fun removeItem(position: Int) {
synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList)
if (list.removeAt(position) != null) {
asyncListDiffer.submitList(list)
}
}
}
fun removeItem(item: ITEM) {
synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList)
if (list.remove(item)) {
asyncListDiffer.submitList(list)
}
}
}
fun removeItems(items: List<ITEM>) {
synchronized(lock) {
val list = ArrayList(asyncListDiffer.currentList)
if (list.removeAll(items)) {
asyncListDiffer.submitList(list)
}
}
}
fun swapItem(oldPosition: Int, newPosition: Int) {
synchronized(lock) {
val size = itemCount
if (oldPosition in 0 until size && newPosition in 0 until size) {
Collections.swap(asyncListDiffer.currentList, oldPosition, newPosition)
notifyItemChanged(oldPosition)
notifyItemChanged(newPosition)
}
}
}
fun updateItem(item: ITEM) =
synchronized(lock) {
val index = asyncListDiffer.currentList.indexOf(item)
if (index >= 0) {
asyncListDiffer.currentList[index] = item
notifyItemChanged(index)
}
}
fun updateItem(position: Int, payload: Any) =
synchronized(lock) {
val size = itemCount
if (position in 0 until size) {
notifyItemChanged(position, payload)
}
}
fun updateItems(fromPosition: Int, toPosition: Int, payloads: Any) =
synchronized(lock) {
val size = itemCount
if (fromPosition in 0 until size && toPosition in 0 until size) {
notifyItemRangeChanged(
fromPosition,
toPosition - fromPosition + 1,
payloads
)
}
}
fun clearItems() =
synchronized(lock) {
asyncListDiffer.submitList(arrayListOf())
}
fun isEmpty() = asyncListDiffer.currentList.isEmpty()
fun isNotEmpty() = asyncListDiffer.currentList.isNotEmpty()
fun getItem(position: Int): ITEM? = asyncListDiffer.currentList.getOrNull(position)
fun getItems(): List<ITEM> = asyncListDiffer.currentList
/**
* grid 模式下使用
*/
protected open fun getSpanSize(viewType: Int, position: Int) = 1
final override fun getItemCount() = getItems().size
final override fun getItemViewType(position: Int): Int {
return 0
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val holder = ItemViewHolder(getViewBinding(parent))
@Suppress("UNCHECKED_CAST")
registerListener(holder, (holder.binding as VB))
if (itemClickListener != null) {
holder.itemView.setOnClickListener {
getItem(holder.layoutPosition)?.let {
itemClickListener?.invoke(holder, it)
}
}
}
if (itemLongClickListener != null) {
holder.itemView.setOnLongClickListener {
getItem(holder.layoutPosition)?.let {
itemLongClickListener?.invoke(holder, it) ?: true
} ?: true
}
}
return holder
}
protected abstract fun getViewBinding(parent: ViewGroup): VB
final override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {}
@Suppress("UNCHECKED_CAST")
final override fun onBindViewHolder(
holder: ItemViewHolder,
position: Int,
payloads: MutableList<Any>
) {
getItem(holder.layoutPosition)?.let {
convert(holder, (holder.binding as VB), it, payloads)
}
}
override fun onViewAttachedToWindow(holder: ItemViewHolder) {
super.onViewAttachedToWindow(holder)
addAnimation(holder)
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
val manager = recyclerView.layoutManager
if (manager is GridLayoutManager) {
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return getSpanSize(getItemViewType(position), position)
}
}
}
}
private fun addAnimation(holder: ItemViewHolder) {
itemAnimation?.let {
if (it.itemAnimEnabled) {
if (!it.itemAnimFirstOnly || holder.layoutPosition > it.itemAnimStartPosition) {
startAnimation(holder, it)
it.itemAnimStartPosition = holder.layoutPosition
}
}
}
}
protected open fun startAnimation(holder: ItemViewHolder, item: ItemAnimation) {
item.itemAnimation?.let {
for (anim in it.getAnimators(holder.itemView)) {
anim.setDuration(item.itemAnimDuration).start()
anim.interpolator = item.itemAnimInterpolator
}
}
} }
/** /**
@ -42,4 +277,5 @@ abstract class SimpleRecyclerAdapter<ITEM, VB : ViewBinding>(context: Context) :
* 注册事件 * 注册事件
*/ */
abstract fun registerListener(holder: ItemViewHolder, binding: VB) abstract fun registerListener(holder: ItemViewHolder, binding: VB)
} }

@ -63,6 +63,7 @@ object PreferKey {
const val autoChangeSource = "autoChangeSource" const val autoChangeSource = "autoChangeSource"
const val importKeepName = "importKeepName" const val importKeepName = "importKeepName"
const val screenDirection = "screenDirection" const val screenDirection = "screenDirection"
const val syncBookProgress = "syncBookProgress"
const val cPrimary = "colorPrimary" const val cPrimary = "colorPrimary"
const val cAccent = "colorAccent" const val cAccent = "colorAccent"

@ -147,6 +147,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val importKeepName get() = context.getPrefBoolean(PreferKey.importKeepName) val importKeepName get() = context.getPrefBoolean(PreferKey.importKeepName)
val syncBookProgress get() = context.getPrefBoolean(PreferKey.syncBookProgress, true)
private fun getPrefUserAgent(): String { private fun getPrefUserAgent(): String {
val ua = context.getPrefString(PreferKey.userAgent) val ua = context.getPrefString(PreferKey.userAgent)
if (ua.isNullOrBlank()) { if (ua.isNullOrBlank()) {

@ -91,6 +91,7 @@ object ReadBook {
} }
fun uploadProgress() { fun uploadProgress() {
if (!AppConfig.syncBookProgress) return
book?.let { book?.let {
BookWebDav.uploadBookProgress(it) BookWebDav.uploadBookProgress(it)
} }

@ -163,7 +163,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
} }
adapter.cacheChapters[book.bookUrl] = chapterCaches adapter.cacheChapters[book.bookUrl] = chapterCaches
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.notifyItemRangeChanged(0, adapter.getActualItemCount(), true) adapter.notifyItemRangeChanged(0, adapter.itemCount, true)
} }
} }
} }
@ -179,7 +179,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
menu?.applyTint(this) menu?.applyTint(this)
} }
adapter.downloadMap = it adapter.downloadMap = it
adapter.notifyItemRangeChanged(0, adapter.getActualItemCount(), true) adapter.notifyItemRangeChanged(0, adapter.itemCount, true)
} }
observeEvent<BookChapter>(EventBus.SAVE_CONTENT) { observeEvent<BookChapter>(EventBus.SAVE_CONTENT) {
adapter.cacheChapters[it.bookUrl]?.add(it.url) adapter.cacheChapters[it.bookUrl]?.add(it.url)

@ -3,8 +3,8 @@ package io.legado.app.ui.book.explore
import android.content.Context import android.content.Context
import android.view.ViewGroup import android.view.ViewGroup
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.CommonRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemSearchBinding import io.legado.app.databinding.ItemSearchBinding
@ -13,7 +13,7 @@ import io.legado.app.utils.visible
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
class ExploreShowAdapter(context: Context, val callBack: CallBack) : class ExploreShowAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<SearchBook, ItemSearchBinding>(context) { CommonRecyclerAdapter<SearchBook, ItemSearchBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemSearchBinding { override fun getViewBinding(parent: ViewGroup): ItemSearchBinding {
return ItemSearchBinding.inflate(inflater, parent, false) return ItemSearchBinding.inflate(inflater, parent, false)

@ -163,17 +163,18 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} }
fun syncBookProgress(book: Book) { fun syncBookProgress(book: Book) {
execute { if (!AppConfig.syncBookProgress)
BookWebDav.getBookProgress(book)?.let { progress -> execute {
if (progress.durChapterIndex < book.durChapterIndex || BookWebDav.getBookProgress(book)?.let { progress ->
(progress.durChapterIndex == book.durChapterIndex && progress.durChapterPos < book.durChapterPos) if (progress.durChapterIndex < book.durChapterIndex ||
) { (progress.durChapterIndex == book.durChapterIndex && progress.durChapterPos < book.durChapterPos)
processLiveData.postValue(progress) ) {
} else { processLiveData.postValue(progress)
ReadBook.setProgress(progress) } else {
ReadBook.setProgress(progress)
}
} }
} }
}
} }
fun changeTo(newBook: Book) { fun changeTo(newBook: Book) {

@ -2,8 +2,8 @@ package io.legado.app.ui.book.read.config
import android.content.Context import android.content.Context
import android.view.ViewGroup import android.view.ViewGroup
import io.legado.app.base.adapter.CommonRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.databinding.ItemBgImageBinding import io.legado.app.databinding.ItemBgImageBinding
import io.legado.app.help.ImageLoader import io.legado.app.help.ImageLoader
@ -13,7 +13,7 @@ import org.jetbrains.anko.sdk27.listeners.onClick
import java.io.File import java.io.File
class BgAdapter(context: Context, val textColor: Int) : class BgAdapter(context: Context, val textColor: Int) :
SimpleRecyclerAdapter<String, ItemBgImageBinding>(context) { CommonRecyclerAdapter<String, ItemBgImageBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemBgImageBinding { override fun getViewBinding(parent: ViewGroup): ItemBgImageBinding {
return ItemBgImageBinding.inflate(inflater, parent, false) return ItemBgImageBinding.inflate(inflater, parent, false)

@ -6,8 +6,8 @@ import android.view.*
import androidx.core.view.get import androidx.core.view.get
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.CommonRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.databinding.DialogReadBookStyleBinding import io.legado.app.databinding.DialogReadBookStyleBinding
import io.legado.app.databinding.ItemReadStyleBinding import io.legado.app.databinding.ItemReadStyleBinding
@ -207,7 +207,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
} }
inner class StyleAdapter : inner class StyleAdapter :
SimpleRecyclerAdapter<ReadBookConfig.Config, ItemReadStyleBinding>(requireContext()) { CommonRecyclerAdapter<ReadBookConfig.Config, ItemReadStyleBinding>(requireContext()) {
override fun getViewBinding(parent: ViewGroup): ItemReadStyleBinding { override fun getViewBinding(parent: ViewGroup): ItemReadStyleBinding {
return ItemReadStyleBinding.inflate(inflater, parent, false) return ItemReadStyleBinding.inflate(inflater, parent, false)

@ -151,7 +151,7 @@ class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
cbName.onClick { cbName.onClick {
getItem(holder.layoutPosition)?.let { httpTTS -> getItem(holder.layoutPosition)?.let { httpTTS ->
engineId = httpTTS.id engineId = httpTTS.id
notifyItemRangeChanged(0, getActualItemCount()) notifyItemRangeChanged(0, itemCount)
} }
} }
ivEdit.onClick { ivEdit.onClick {

@ -117,7 +117,7 @@ class SearchContentActivity :
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book))
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.notifyItemRangeChanged(0, adapter.getActualItemCount(), true) adapter.notifyItemRangeChanged(0, adapter.itemCount, true)
} }
} }
} }

@ -399,7 +399,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
override fun upCountView() { override fun upCountView() {
binding.selectActionBar binding.selectActionBar
.upCountView(adapter.getSelection().size, adapter.getActualItemCount()) .upCountView(adapter.getSelection().size, adapter.itemCount)
} }
override fun onQueryTextChange(newText: String?): Boolean { override fun onQueryTextChange(newText: String?): Boolean {

@ -102,7 +102,7 @@ class ChapterListFragment : VMBaseFragment<ChapterListViewModel>(R.layout.fragme
launch(IO) { launch(IO) {
adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book))
withContext(Main) { withContext(Main) {
adapter.notifyItemRangeChanged(0, adapter.getActualItemCount(), true) adapter.notifyItemRangeChanged(0, adapter.itemCount, true)
} }
} }
} }

@ -134,7 +134,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
} }
fun getBooksCount(): Int { fun getBooksCount(): Int {
return booksAdapter.getActualItemCount() return booksAdapter.itemCount
} }
override fun open(book: Book) { override fun open(book: Book) {

@ -39,7 +39,7 @@ class ExploreAdapter(context: Context, private val scope: CoroutineScope, val ca
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
with(binding) { with(binding) {
if (holder.layoutPosition == getActualItemCount() - 1) { if (holder.layoutPosition == itemCount - 1) {
root.setPadding(16.dp, 12.dp, 16.dp, 12.dp) root.setPadding(16.dp, 12.dp, 16.dp, 12.dp)
} else { } else {
root.setPadding(16.dp, 12.dp, 16.dp, 0) root.setPadding(16.dp, 12.dp, 16.dp, 0)

@ -5,8 +5,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.CommonRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.databinding.ItemRssBinding import io.legado.app.databinding.ItemRssBinding
import io.legado.app.help.ImageLoader import io.legado.app.help.ImageLoader
@ -14,7 +14,7 @@ import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.sdk27.listeners.onLongClick import org.jetbrains.anko.sdk27.listeners.onLongClick
class RssAdapter(context: Context, val callBack: CallBack) : class RssAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<RssSource, ItemRssBinding>(context) { CommonRecyclerAdapter<RssSource, ItemRssBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemRssBinding { override fun getViewBinding(parent: ViewGroup): ItemRssBinding {
return ItemRssBinding.inflate(inflater, parent, false) return ItemRssBinding.inflate(inflater, parent, false)

@ -281,7 +281,7 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
override fun upCountView() { override fun upCountView() {
binding.selectActionBar.upCountView( binding.selectActionBar.upCountView(
adapter.getSelection().size, adapter.getSelection().size,
adapter.getActualItemCount() adapter.itemCount
) )
} }

@ -2,12 +2,12 @@ package io.legado.app.ui.rss.article
import android.content.Context import android.content.Context
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.CommonRecyclerAdapter
import io.legado.app.data.entities.RssArticle import io.legado.app.data.entities.RssArticle
abstract class BaseRssArticlesAdapter<VB : ViewBinding>(context: Context, val callBack: CallBack) : abstract class BaseRssArticlesAdapter<VB : ViewBinding>(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<RssArticle, VB>(context) { CommonRecyclerAdapter<RssArticle, VB>(context) {
interface CallBack { interface CallBack {
val isGridLayout: Boolean val isGridLayout: Boolean

@ -221,7 +221,7 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
override fun upCountView() { override fun upCountView() {
binding.selectActionBar.upCountView( binding.selectActionBar.upCountView(
adapter.getSelection().size, adapter.getSelection().size,
adapter.getActualItemCount() adapter.itemCount
) )
} }

@ -790,5 +790,7 @@
<string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string> <string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string>
<string name="get_book_progress">拉取云端进度</string> <string name="get_book_progress">拉取云端进度</string>
<string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string> <string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string>
<string name="sync_book_progress_t">同步阅读进度</string>
<string name="sync_book_progress_s">进入退出阅读界面时同步阅读进度</string>
</resources> </resources>

@ -791,5 +791,7 @@
<string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string> <string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string>
<string name="get_book_progress">拉取云端进度</string> <string name="get_book_progress">拉取云端进度</string>
<string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string> <string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string>
<string name="sync_book_progress_t">同步阅读进度</string>
<string name="sync_book_progress_s">进入退出阅读界面时同步阅读进度</string>
</resources> </resources>

@ -794,5 +794,7 @@
<string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string> <string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string>
<string name="get_book_progress">拉取云端进度</string> <string name="get_book_progress">拉取云端进度</string>
<string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string> <string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string>
<string name="sync_book_progress_t">同步阅读进度</string>
<string name="sync_book_progress_s">进入退出阅读界面时同步阅读进度</string>
</resources> </resources>

@ -797,5 +797,7 @@
<string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string> <string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string>
<string name="get_book_progress">拉取云端进度</string> <string name="get_book_progress">拉取云端进度</string>
<string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string> <string name="current_progress_exceeds_cloud">当前进度超过云端进度,是否同步?</string>
<string name="sync_book_progress_t">同步阅读进度</string>
<string name="sync_book_progress_s">进入退出阅读界面时同步阅读进度</string>
</resources> </resources>

@ -36,6 +36,15 @@
app:allowDividerBelow="false" app:allowDividerBelow="false"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference
android:key="syncBookProgress"
android:defaultValue="true"
android:title="@string/sync_book_progress_t"
android:summary="@string/sync_book_progress_s"
app:allowDividerAbove="false"
app:allowDividerBelow="false"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference <io.legado.app.ui.widget.prefs.SwitchPreference
android:key="webDavCacheBackup" android:key="webDavCacheBackup"
android:defaultValue="false" android:defaultValue="false"

Loading…
Cancel
Save