taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

commit 9077c822a1c6b934e91734427dc09209a0c21a90
parent 662fe62a9ce5204eb86620932e5f97b8c53e8344
Author: Iván Ávalos <avalos@disroot.org>
Date:   Wed, 17 Jul 2024 12:40:32 -0600

[wallet] Optimize adapter updates with diff

Diffstat:
Mwallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt | 37+++++++++++++++++++++++++++++++++----
Mwallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt | 2+-
Mwallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt | 38+++++++++++++++++++++++++++++++++-----
Mwallet/src/main/java/net/taler/wallet/payment/ProductAdapter.kt | 40++++++++++++++++++++++++++++++++++------
Mwallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt | 2+-
Mwallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt | 52++++++++++++++++++++++++++++++++--------------------
Mwallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt | 4++--
7 files changed, 136 insertions(+), 39 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt @@ -22,6 +22,7 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.TextView +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.Adapter import net.taler.wallet.R @@ -52,9 +53,14 @@ class BalanceAdapter(private val listener: BalanceClickListener) : Adapter<Balan holder.bind(item) } - fun setItems(items: List<BalanceItem>) { - this.items = items - this.notifyDataSetChanged() + fun update(newItems: List<BalanceItem>) { + val oldItems = this.items + + val diffCallback = BalanceDiffCallback(oldItems, newItems) + val diffResult = DiffUtil.calculateDiff(diffCallback) + diffResult.dispatchUpdatesTo(this) + + this.items = newItems } inner class BalanceViewHolder(private val v: View) : RecyclerView.ViewHolder(v) { @@ -97,5 +103,27 @@ class BalanceAdapter(private val listener: BalanceClickListener) : Adapter<Balan } } } - } + +internal class BalanceDiffCallback( + private val oldList: List<BalanceItem>, + private val newList: List<BalanceItem>, +): DiffUtil.Callback() { + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val old = oldList[oldItemPosition] + val new = newList[newItemPosition] + + return old.scopeInfo == new.scopeInfo + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val old = oldList[oldItemPosition] + val new = newList[newItemPosition] + + return old == new + } +} +\ No newline at end of file diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt @@ -83,7 +83,7 @@ class BalancesFragment : Fragment(), ui.mainEmptyState.visibility = VISIBLE ui.mainList.visibility = GONE } else { - balancesAdapter.setItems(state.balances) + balancesAdapter.update(state.balances) ui.mainEmptyState.visibility = INVISIBLE ui.mainList.fadeIn() } diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt @@ -24,6 +24,7 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.TextView import androidx.appcompat.widget.PopupMenu +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.Adapter import net.taler.wallet.R @@ -42,7 +43,7 @@ internal class ExchangeAdapter( private val listener: ExchangeClickListener, ) : Adapter<ExchangeItemViewHolder>() { - private val items = ArrayList<ExchangeItem>() + private var items = emptyList<ExchangeItem>() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExchangeItemViewHolder { val view = LayoutInflater.from(parent.context) @@ -57,9 +58,13 @@ internal class ExchangeAdapter( } fun update(newItems: List<ExchangeItem>) { - items.clear() - items.addAll(newItems) - notifyDataSetChanged() + val oldItems = this.items + + val diffCallback = ExchangeDiffCallback(oldItems, newItems) + val diffResult = DiffUtil.calculateDiff(diffCallback) + diffResult.dispatchUpdatesTo(this) + + items = newItems } internal inner class ExchangeItemViewHolder(v: View) : RecyclerView.ViewHolder(v) { @@ -114,5 +119,27 @@ internal class ExchangeAdapter( show() } } - } + +internal class ExchangeDiffCallback( + private val oldList: List<ExchangeItem>, + private val newList: List<ExchangeItem>, +): DiffUtil.Callback() { + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val old = oldList[oldItemPosition] + val new = newList[newItemPosition] + + return old.exchangeBaseUrl == new.exchangeBaseUrl + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val old = oldList[oldItemPosition] + val new = newList[newItemPosition] + + return old == new + } +} +\ No newline at end of file diff --git a/wallet/src/main/java/net/taler/wallet/payment/ProductAdapter.kt b/wallet/src/main/java/net/taler/wallet/payment/ProductAdapter.kt @@ -26,6 +26,7 @@ import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder import net.taler.common.ContractProduct @@ -39,7 +40,7 @@ internal interface ProductImageClickListener { internal class ProductAdapter(private val listener: ProductImageClickListener) : RecyclerView.Adapter<ProductViewHolder>() { - private val items = ArrayList<ContractProduct>() + private var items = emptyList<ContractProduct>() override fun getItemCount() = items.size @@ -53,10 +54,14 @@ internal class ProductAdapter(private val listener: ProductImageClickListener) : holder.bind(items[position]) } - fun setItems(items: List<ContractProduct>) { - this.items.clear() - this.items.addAll(items) - notifyDataSetChanged() + fun update(newItems: List<ContractProduct>) { + val oldItems = this.items + + val diffCallback = ProductDiffCallback(oldItems, newItems) + val diffResult = DiffUtil.calculateDiff(diffCallback) + diffResult.dispatchUpdatesTo(this) + + items = newItems } internal inner class ProductViewHolder(v: View) : ViewHolder(v) { @@ -88,5 +93,27 @@ internal class ProductAdapter(private val listener: ProductImageClickListener) : } ?: GONE } } - } + +internal class ProductDiffCallback( + private val oldList: List<ContractProduct>, + private val newList: List<ContractProduct>, +): DiffUtil.Callback() { + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val old = oldList[oldItemPosition] + val new = newList[newItemPosition] + + return old.productId == new.productId + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val old = oldList[oldItemPosition] + val new = newList[newItemPosition] + + return old == new + } +} +\ No newline at end of file diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt @@ -155,7 +155,7 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { private fun showOrder(contractTerms: ContractTerms, amount: Amount, totalFees: Amount? = null) { ui.details.orderView.text = contractTerms.summary - adapter.setItems(contractTerms.products) + adapter.update(contractTerms.products) ui.details.productsList.fadeIn() ui.bottom.totalView.text = amount.toString() if (totalFees != null && !totalFees.isZero()) { diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt @@ -16,7 +16,6 @@ package net.taler.wallet.transactions -import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.MotionEvent @@ -76,25 +75,32 @@ internal class TransactionAdapter( holder.bind(transaction, tracker.isSelected(transaction.transactionId)) } - @SuppressLint("NotifyDataSetChanged") - fun setCurrencySpec(spec: CurrencySpecification?) { - this.currencySpec = spec - this.notifyDataSetChanged() - } - - @SuppressLint("NotifyDataSetChanged") - fun update(updatedTransactions: List<Transaction>? = null, networkAvailable: Boolean? = null) { - updatedTransactions?.let { - val diffCallback = TransactionDiffCallback(transactions, updatedTransactions) - val diffResult = DiffUtil.calculateDiff(diffCallback) - this.transactions = it - diffResult.dispatchUpdatesTo(this) - } - - networkAvailable?.let { - this.networkAvailable = it - this.notifyDataSetChanged() - } + fun update( + updatedTransactions: List<Transaction>? = null, + updatedNetworkAvailable: Boolean? = null, + updatedCurrencySpec: CurrencySpecification? = null, + ) { + val oldTransactions = transactions + val newTransactions = updatedTransactions ?: oldTransactions + val oldNetworkAvailable = networkAvailable + val newNetworkAvailable = updatedNetworkAvailable ?: oldNetworkAvailable + val oldCurrencySpec = currencySpec + val newCurrencySpec = updatedCurrencySpec ?: oldCurrencySpec + + val diffCallback = TransactionDiffCallback( + oldTransactions, + newTransactions, + oldNetworkAvailable, + newNetworkAvailable, + oldCurrencySpec, + newCurrencySpec, + ) + val diffResult = DiffUtil.calculateDiff(diffCallback) + diffResult.dispatchUpdatesTo(this) + + transactions = newTransactions + networkAvailable = newNetworkAvailable + currencySpec = newCurrencySpec } fun selectAll() = transactions.forEach { @@ -274,6 +280,10 @@ internal class TransactionLookup( internal class TransactionDiffCallback( private val oldList: List<Transaction>, private val newList: List<Transaction>, + private val oldNetworkAvailable: Boolean?, + private val newNetworkAvailable: Boolean?, + private val oldCurrencySpec: CurrencySpecification?, + private val newCurrencySpec: CurrencySpecification?, ): DiffUtil.Callback() { override fun getOldListSize() = oldList.size @@ -292,5 +302,7 @@ internal class TransactionDiffCallback( return oldTx.txState == newTx.txState && oldTx.error == newTx.error + && oldNetworkAvailable == newNetworkAvailable + && oldCurrencySpec == newCurrencySpec } } \ No newline at end of file diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt @@ -120,7 +120,7 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. balances.find { it.scopeInfo == scopeInfo }?.let { balance -> ui.actionsBar.amount.text = balance.available.toString(showSymbol = false) - transactionAdapter.setCurrencySpec(balance.available.spec) + transactionAdapter.update(updatedCurrencySpec = balance.available.spec) } } @@ -134,7 +134,7 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. } networkManager.networkStatus.observe(viewLifecycleOwner) { state -> - transactionAdapter.update(networkAvailable = state) + transactionAdapter.update(updatedNetworkAvailable = state) } ui.actionsBar.sendButton.setOnClickListener {