Partial ProductAdapter updates, drop direct bindViewHolder calls

This commit is contained in:
kitsunyan
2020-07-04 12:22:22 +03:00
parent 930e195772
commit 0d4aedad87
2 changed files with 87 additions and 75 deletions
@@ -284,6 +284,8 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
} }
} }
private enum class Payload { REFRESH, STATUS }
private class HeaderViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { private class HeaderViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val icon = itemView.findViewById<ImageView>(R.id.icon)!! val icon = itemView.findViewById<ImageView>(R.id.icon)!!
val name = itemView.findViewById<TextView>(R.id.name)!! val name = itemView.findViewById<TextView>(R.id.name)!!
@@ -781,33 +783,39 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
private var action: Action? = null private var action: Action? = null
fun setAction(recyclerView: RecyclerView, action: Action?) { fun setAction(action: Action?) {
if (this.action != action) { if (this.action != action) {
val translate = this.action == null || action == null ||
this.action == Action.CANCEL || action == Action.CANCEL
this.action = action this.action = action
if (items.getOrNull(0) is Item.HeaderItem) { val index = items.indexOfFirst { it is Item.HeaderItem }
val holder = recyclerView.findViewHolderForAdapterPosition(0) if (index >= 0) {
holder?.let { bindViewHolder(holder, 0) } if (translate) {
notifyItemChanged(index)
} else {
notifyItemChanged(index, Payload.REFRESH)
}
} }
} }
} }
private var status: Status? = null private var status: Status? = null
fun setStatus(recyclerView: RecyclerView, status: Status?) { fun setStatus(status: Status?) {
val notify = (this.status == null) != (status == null) val translate = (this.status == null) != (status == null)
if (this.status != status) { if (this.status != status) {
this.status = status this.status = status
if (items.getOrNull(0) is Item.HeaderItem) { val index = items.indexOfFirst { it is Item.HeaderItem }
if (notify) { if (index >= 0) {
notifyItemChanged(0) if (translate) {
notifyItemChanged(index)
val from = items.indexOfFirst { it is Item.ReleaseItem } val from = items.indexOfFirst { it is Item.ReleaseItem }
val to = items.indexOfLast { it is Item.ReleaseItem } val to = items.indexOfLast { it is Item.ReleaseItem }
if (from in 0 .. to) { if (from in 0 .. to) {
notifyItemRangeChanged(from, to - from + 1) notifyItemRangeChanged(from, to - from + 1)
} }
} else { } else {
val holder = recyclerView.findViewHolderForAdapterPosition(0) notifyItemChanged(index, Payload.STATUS)
holder?.let { bindViewHolder(holder, 0) }
} }
} }
} }
@@ -840,8 +848,6 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
} }
ViewType.SWITCH -> SwitchViewHolder(parent.inflate(R.layout.switch_item)).apply { ViewType.SWITCH -> SwitchViewHolder(parent.inflate(R.layout.switch_item)).apply {
itemView.setOnClickListener { itemView.setOnClickListener {
val positions = items.asSequence().mapIndexedNotNull { index, item -> if (item is Item.HeaderItem ||
item is Item.SwitchItem) index else null }
val switchItem = items[adapterPosition] as Item.SwitchItem val switchItem = items[adapterPosition] as Item.SwitchItem
val productPreference = when (switchItem.switchType) { val productPreference = when (switchItem.switchType) {
SwitchType.IGNORE_ALL_UPDATES -> { SwitchType.IGNORE_ALL_UPDATES -> {
@@ -853,13 +859,8 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
} }
} }
ProductPreferences[switchItem.packageName] = productPreference ProductPreferences[switchItem.packageName] = productPreference
val recyclerView = itemView.parent as RecyclerView items.asSequence().mapIndexedNotNull { index, item -> if (item is Item.HeaderItem ||
positions.forEach { item is Item.SectionItem) index else null }.forEach { notifyItemChanged(it, Payload.REFRESH) }
val holder = recyclerView.findViewHolderForAdapterPosition(it)
if (holder != null) {
bindViewHolder(holder, it)
}
}
callbacks.onPreferenceChanged(productPreference) callbacks.onPreferenceChanged(productPreference)
} }
} }
@@ -871,14 +872,14 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
expanded += sectionItem.expandType expanded += sectionItem.expandType
items[position] = Item.SectionItem(sectionItem.sectionType, sectionItem.expandType, emptyList(), items[position] = Item.SectionItem(sectionItem.sectionType, sectionItem.expandType, emptyList(),
sectionItem.items.size + sectionItem.collapseCount) sectionItem.items.size + sectionItem.collapseCount)
bindViewHolder(this, position) notifyItemChanged(adapterPosition, Payload.REFRESH)
items.addAll(position + 1, sectionItem.items) items.addAll(position + 1, sectionItem.items)
notifyItemRangeInserted(position + 1, sectionItem.items.size) notifyItemRangeInserted(position + 1, sectionItem.items.size)
} else if (sectionItem.collapseCount > 0) { } else if (sectionItem.collapseCount > 0) {
expanded -= sectionItem.expandType expanded -= sectionItem.expandType
items[position] = Item.SectionItem(sectionItem.sectionType, sectionItem.expandType, items[position] = Item.SectionItem(sectionItem.sectionType, sectionItem.expandType,
items.subList(position + 1, position + 1 + sectionItem.collapseCount).toList(), 0) items.subList(position + 1, position + 1 + sectionItem.collapseCount).toList(), 0)
bindViewHolder(this, position) notifyItemChanged(adapterPosition, Payload.REFRESH)
repeat(sectionItem.collapseCount) { items.removeAt(position + 1) } repeat(sectionItem.collapseCount) { items.removeAt(position + 1) }
notifyItemRangeRemoved(position + 1, sectionItem.collapseCount) notifyItemRangeRemoved(position + 1, sectionItem.collapseCount)
} }
@@ -900,7 +901,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
items.addAll(position, expandItem.items) items.addAll(position, expandItem.items)
if (position > 0) { if (position > 0) {
// Update decorations // Update decorations
notifyItemChanged(position - 1) notifyItemChanged(position - 1, Payload.REFRESH)
} }
notifyItemRemoved(position) notifyItemRemoved(position)
notifyItemRangeInserted(position, expandItem.items.size) notifyItemRangeInserted(position, expandItem.items.size)
@@ -944,6 +945,10 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
onBindViewHolder(holder, position, emptyList())
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: List<Any>) {
val context = holder.itemView.context val context = holder.itemView.context
val item = items[position] val item = items[position]
when (getItemEnumViewType(position)) { when (getItemEnumViewType(position)) {
@@ -951,54 +956,60 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
holder as HeaderViewHolder holder as HeaderViewHolder
item as Item.HeaderItem item as Item.HeaderItem
val installedItem = installedItem val installedItem = installedItem
if (item.product.icon.isNotEmpty()) { val updateStatus = Payload.STATUS in payloads
holder.icon.load(PicassoDownloader.createIconUri(holder.icon, item.product.icon, item.repository)) { val updateAll = !updateStatus
placeholder(holder.progressIcon) if (updateAll) {
error(holder.defaultIcon) if (item.product.icon.isNotEmpty()) {
holder.icon.load(PicassoDownloader.createIconUri(holder.icon, item.product.icon, item.repository)) {
placeholder(holder.progressIcon)
error(holder.defaultIcon)
}
} else {
holder.icon.clear()
holder.icon.setImageDrawable(holder.defaultIcon)
}
holder.name.text = item.product.name
val canUpdate = item.product.canUpdate(installedItem) &&
!ProductPreferences[item.product.packageName].shouldIgnoreUpdate(item.product.versionCode)
val version = (if (canUpdate) item.product.version else installedItem?.version)?.nullIfEmpty()
?: item.product.version.nullIfEmpty()
holder.version.text = version?.let { context.getString(R.string.version_format, it) }
?: context.getString(R.string.unknown)
holder.packageName.text = item.product.packageName
val action = action
holder.action.visibility = if (action == null) View.GONE else View.VISIBLE
if (action != null) {
holder.action.setText(action.titleResId)
}
if (Android.sdk(22)) {
holder.action.backgroundTintList = if (action == Action.CANCEL)
holder.actionTintCancel else holder.actionTintNormal
} }
} else {
holder.icon.clear()
holder.icon.setImageDrawable(holder.defaultIcon)
} }
holder.name.text = item.product.name if (updateAll || updateStatus) {
val canUpdate = item.product.canUpdate(installedItem) && val status = status
!ProductPreferences[item.product.packageName].shouldIgnoreUpdate(item.product.versionCode) holder.statusLayout.visibility = if (status != null) View.VISIBLE else View.GONE
val version = (if (canUpdate) item.product.version else installedItem?.version)?.nullIfEmpty() if (status != null) {
?: item.product.version.nullIfEmpty() when (status) {
holder.version.text = version?.let { context.getString(R.string.version_format, it) } is Status.Pending -> {
?: context.getString(R.string.unknown) holder.status.setText(R.string.waiting_to_start_download)
holder.packageName.text = item.product.packageName holder.progress.isIndeterminate = true
val action = action
val status = status
holder.action.visibility = if (action == null) View.GONE else View.VISIBLE
if (action != null) {
holder.action.setText(action.titleResId)
}
if (Android.sdk(22)) {
holder.action.backgroundTintList = if (action == Action.CANCEL)
holder.actionTintCancel else holder.actionTintNormal
}
holder.statusLayout.visibility = if (status != null) View.VISIBLE else View.GONE
if (status != null) {
when (status) {
is Status.Pending -> {
holder.status.setText(R.string.waiting_to_start_download)
holder.progress.isIndeterminate = true
}
is Status.Connecting -> {
holder.status.setText(R.string.connecting)
holder.progress.isIndeterminate = true
}
is Status.Downloading -> {
holder.status.text = context.getString(R.string.downloading_format, if (status.total == null)
status.read.formatSize() else "${status.read.formatSize()} / ${status.total.formatSize()}")
holder.progress.isIndeterminate = status.total == null
if (status.total != null) {
holder.progress.progress = (holder.progress.max.toFloat() * status.read / status.total).roundToInt()
} }
Unit is Status.Connecting -> {
} holder.status.setText(R.string.connecting)
}::class holder.progress.isIndeterminate = true
}
is Status.Downloading -> {
holder.status.text = context.getString(R.string.downloading_format, if (status.total == null)
status.read.formatSize() else "${status.read.formatSize()} / ${status.total.formatSize()}")
holder.progress.isIndeterminate = status.total == null
if (status.total != null) {
holder.progress.progress = (holder.progress.max.toFloat() * status.read / status.total).roundToInt()
}
Unit
}
}::class
}
} }
Unit Unit
} }
@@ -148,8 +148,12 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
if (firstChanged || productChanged || installedItemChanged) { if (firstChanged || productChanged || installedItemChanged) {
layoutManagerState?.let { recyclerView?.layoutManager!!.onRestoreInstanceState(it) } layoutManagerState?.let { recyclerView?.layoutManager!!.onRestoreInstanceState(it) }
layoutManagerState = null layoutManagerState = null
this.products = products if (firstChanged || productChanged) {
this.installedItem = installedItem.value this.products = products
}
if (firstChanged || installedItemChanged) {
this.installedItem = installedItem.value
}
val recyclerView = recyclerView!! val recyclerView = recyclerView!!
val adapter = recyclerView.adapter as ProductAdapter val adapter = recyclerView.adapter as ProductAdapter
if (firstChanged || productChanged) { if (firstChanged || productChanged) {
@@ -231,7 +235,7 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
installedItem != null -> ProductAdapter.Action.DETAILS installedItem != null -> ProductAdapter.Action.DETAILS
else -> null else -> null
} }
(recyclerView.adapter as ProductAdapter).setAction(recyclerView, adapterAction) (recyclerView.adapter as ProductAdapter).setAction(adapterAction)
Action.values().find { it.adapterAction == adapterAction }?.let { actions -= it } Action.values().find { it.adapterAction == adapterAction }?.let { actions -= it }
} }
@@ -256,10 +260,7 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
this.downloading = downloading this.downloading = downloading
updateButtons() updateButtons()
} }
val recyclerView = recyclerView (recyclerView?.adapter as? ProductAdapter)?.setStatus(status)
if (recyclerView != null) {
(recyclerView.adapter as ProductAdapter).setStatus(recyclerView, status)
}
if (state is DownloadService.State.Success && isResumed) { if (state is DownloadService.State.Success && isResumed) {
state.consume() state.consume()
screenActivity.startPackageInstaller(state.release.cacheFileName) screenActivity.startPackageInstaller(state.release.cacheFileName)